抽象工厂模式
抽象工厂模式
抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
介绍
意图:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
主要解决:主要解决接口选择的问题。
何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
如何解决:在一个产品族里面,定义多个产品。
关键代码:在一个工厂里聚合多个同类产品。
应用实例:工作了,为了参加一些聚会,肯定有两套或多套衣服吧,比如说有商务装(成套,一系列具体产品)、时尚装(成套,一系列具体产品),甚至对于一个家庭来说,可能有商务女装、商务男装、时尚女装、时尚男装,这些也都是成套的,即一系列具体产品。假设一种情况(现实中是不存在的,要不然,没法进入共产主义了,但有利于说明抽象工厂模式),在您的家中,某一个衣柜(具体工厂)只能存放某一种这样的衣服(成套,一系列具体产品),每次拿这种成套的衣服时也自然要从这个衣柜中取出了。用 OOP 的思想去理解,所有的衣柜(具体工厂)都是衣柜类的(抽象工厂)某一个,而每一件成套的衣服又包括具体的上衣(某一具体产品),裤子(某一具体产品),这些具体的上衣其实也都是上衣(抽象产品),具体的裤子也都是裤子(另一个抽象产品)。
优点:当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
使用场景: 1、QQ 换皮肤,一整套一起换。 2、生成不同操作系统的程序。
注意事项:产品族难扩展,产品等级易扩展。
抽象工厂方法模式
工厂方法模式是对简单工厂模式进一步抽象的结果。
假如是不使用反射的工厂方法模式,那么所有的if...
else if...else都放在工厂类中,势必造成工厂类的无限臃肿
这时候就需要工厂方法模式来处理这个问题了。工厂方法模式中,核心的工厂类不再负责所有对象的创建,而是将具体的创建工作交给子类去做。这个类则摇身一变变成了一个抽象工厂角色,仅仅负责给出具体工厂子类必须实现的接口。
这一步的改进,使得系统可以在不修改具体工厂角色的情况下引入新的产品。
角色
在工厂方法模式结构图中包含如下几个角色:
Product(抽象产品):它是定义产品的接口,是工厂方法模式所创建对象的超类型,也就是产品对象的公共父类
ConcreteProduct(具体产品):它实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,具体工厂和具体产品之间一一对应。
Factory(抽象工厂):在抽象工厂类中,声明了工厂方法(Factory Method),用于返回一个产品。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口。
ConcreteFactory(具体工厂):它是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实例。
与简单工厂模式相比,工厂方法模式最重要的区别是引入了抽象工厂角色,抽象工厂可以是接口,也可以是抽象类或者具体类
工厂方法模式的示例
有一个抽象工厂,生产生物的工厂:
1 public interface BiologicalFactory { 2 3 public Biological getbiological(String name); 4 }
它有两个实现类,分别是生产动物的工厂和生产植物的工厂:
1 public class PlantFactory implements BiologicalFactory { 2 3 @Override 4 public Biological getbiological(String name) { 5 if ("apple".equals(name)) { 6 return new Apple(); 7 } else if ("grape".equals(name)) { 8 return new Grape(); 9 } 10 return null; 11 } 12 13 }
1 public class AnimalFactory implements BiologicalFactory{ 2 3 @Override 4 public Biological getbiological(String name) { 5 // TODO Auto-generated method stub 6 if ("panda".equals(name)) { 7 return new Panda(); 8 } else if ("lion".equals(name)) { 9 return new Lion(); 10 } 11 return null; 12 } 13 14 }
抽象产品角色,一个生物:
1 public interface Biological { 2 3 void transform();// 生产 4 }
具体产品角色:
1 public class Apple implements Biological { 2 3 @Override 4 public void transform() { 5 // TODO Auto-generated method stub 6 System.out.println("变出一个苹果"); 7 } 8 9 }
1 public class Grape implements Biological { 2 3 @Override 4 public void transform() { 5 // TODO Auto-generated method stub 6 System.out.println("变出一个葡萄"); 7 } 8 9 }
1 public class Lion implements Biological { 2 3 @Override 4 public void transform() { 5 // TODO Auto-generated method stub 6 System.out.println("变出一只狮子"); 7 } 8 9 }
1 public class Panda implements Biological { 2 3 @Override 4 public void transform() { 5 // TODO Auto-generated method stub 6 System.out.println("变出一只熊猫"); 7 } 8 9 }
模拟客户端调用,实例化出一个具体的工厂角色,根据传入的参数返回不同的产品角色:
1 public class FactoryTest { 2 3 public static void main(String[] args) { 4 String data = ""; 5 BiologicalFactory biologicalFactory1 = new PlantFactory(); 6 7 Biological ef1 = biologicalFactory1.getbiological("apple"); 8 ef1.transform(); 9 10 Biological ef2 = biologicalFactory1.getbiological("grape"); 11 ef2.transform(); 12 13 BiologicalFactory biologicalFactory2 = new AnimalFactory(); 14 15 Biological ef3 = biologicalFactory2.getbiological("lion"); 16 ef3.transform(); 17 18 Biological ef4 = biologicalFactory2.getbiological("panda"); 19 ef4.transform(); 20 } 21 }
返回结果为:
1 变出一个苹果 2 变出一个葡萄 3 变出一只狮子 4 变出一只熊猫
工厂方法模式总结
工厂方法模式是简单工厂模式的延伸,它继承了简单工厂模式的优点,同时还弥补了简单工厂模式的不足。工厂方法模式是使用频率最高的设计模式之一,是很多开源框架和API类库的核心模式。
工厂方法模式的主要优点
-
在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
-
基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够让工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,就正是因为所有的具体工厂类都具有同一抽象父类。
-
使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了,这样,系统的可扩展性也就变得非常好,完全符合"开闭原则"。
工厂方法模式的主要缺点
-
在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
-
由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。
适用场景
-
客户端不知道它所需要的对象的类。在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建,可将具体工厂类的类名存储在配置文件或数据库中。
-
抽象工厂类通过其子类来指定创建哪个对象。在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
工厂模式在Java中的应用及解读
Java集合接口 Collection 中的工厂方法模式
Collection 中的 iterator 方法如下:
1 public interface Collection<E> extends Iterable<E> { 2 Iterator<E> iterator(); 3 // ...省略 4 }
关于 iterator 方法的介绍:
Java的迭代器只在Collection中有,而Map没有迭代器,它有不同的迭代方法;
迭代器的终极目标:就是用统一的方法来迭代不同类型的集合!可能由于不同集合的内部数据结构不尽相同,如果要自己纯手工迭代的话相互之间会有很大的差别,而迭代器的作用就是统一的方法对不同的集合进行迭代,而在迭代器底层隐藏不同集合之间的差异,从而为迭代提供最大的方便
使用用迭代器迭代的步骤: i. 第一步肯定是先获取集合的迭代器:调用集合的iterator方法就能获得,IteratorCollection.iterator(); ii. 使用迭代器的hasNext、next往下迭代
Iterator的常用方法:boolean hasNext():是否还有下一个元素; Object next():取出下一个元素并返回; void remove(); :从容器中删除当前元素,直接会改变容器中的数据
该接口的实现类很多,我们仅看其中一个实现类 java.util.ArrayList
,看其对 iterator
方法的实现
1 public Iterator<E> iterator() { 2 return new Itr(); 3 } 4 5 /** 6 * An optimized version of AbstractList.Itr 7 */ 8 private class Itr implements Iterator<E> { 9 int cursor; // index of next element to return 10 int lastRet = -1; // index of last element returned; -1 if no such 11 int expectedModCount = modCount; 12 13 Itr() {} 14 15 public boolean hasNext() { 16 return cursor != size; 17 } 18 19 @SuppressWarnings("unchecked") 20 public E next() { 21 // ...省略... 22 } 23 24 public void remove() { 25 // ...省略... 26 } 27 28 @Override 29 @SuppressWarnings("unchecked") 30 public void forEachRemaining(Consumer<? super E> consumer) { 31 // ...省略... 32 } 33 34 final void checkForComodification() { 35 // ...省略... 36 } 37 }
Itr
类实现了 iterator
接口,iterator
接口正是 Collection
接口中 iterator
方法的返回类型,其代码如下:
1 public interface Iterator<E> { 2 boolean hasNext(); 3 4 E next(); 5 6 default void remove() { 7 throw new UnsupportedOperationException("remove"); 8 } 9 10 default void forEachRemaining(Consumer<? super E> action) { 11 Objects.requireNonNull(action); 12 while (hasNext()) 13 action.accept(next()); 14 } 15 }
由此可见,Collection
接口扮演了抽象工厂角色,工厂方法为 iterator()
,Collection
的实现类譬如 ArrayList
扮演了具体工厂角色,而抽象产品为 Iterator
接口,具体产品为 Itr
类
ThreadFactory 中的工厂方法模式
这是一个生产线程的接口:
public interface ThreadFactory { /** * Constructs a new {@code Thread}. Implementations may also initialize * priority, name, daemon status, {@code ThreadGroup}, etc. * * @param r a runnable to be executed by new thread instance * @return constructed thread, or {@code null} if the request to * create a thread is rejected */ Thread newThread(Runnable r); }
具体的线程工厂可以implements这个接口并实现newThread(Runnable r)方法,来生产具体线程工厂想要生产的线程。JDK在Executors给开发者提供了一个静态内部类DefaultThreadFactory,当然开发者也可以自行实现这个接口,写自定义的线程工厂。
Spring中的工厂方法模式
FactoryBean
一般情况下,Spring通过反射机制利用<bean>的class属性指定实现类实例化Bean,在某些情况下,实例化Bean过程比较复杂,
如果按照传统的方式,则需要在<bean>中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。
Spring为此提供了一个org.springframework.bean.factory.FactoryBean的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。
FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。
它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。从Spring3.0开始,FactoryBean开始支持泛型,即接口声明改为FactoryBean<T>的形式
1 package org.springframework.beans.factory; 2 3 public interface FactoryBean<T> { 4 T getObject() throws Exception; 5 6 Class<?> getObjectType(); 7 8 boolean isSingleton(); 9 }
在该接口中还定义了以下3个方法:
T getObject():
返回由FactoryBean创建的Bean实例,如果isSingleton()返回true,则该实例会放到Spring容器中单实例缓存池中;
boolean isSingleton():
返回由FactoryBean创建的Bean实例的作用域是singleton还是prototype;
Class<T> getObjectType():
返回FactoryBean创建的Bean类型。
当配置文件中<bean>的class属性配置的实现类是FactoryBean时,通过getBean()方法返回的不是FactoryBean本身,
而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。
1 package com.baobaotao.factorybean; 2 public class Car { 3 private int maxSpeed ; 4 private String brand ; 5 private double price ; 6 public int getMaxSpeed() { 7 return this.maxSpeed ; 8 } 9 public void setMaxSpeed (int maxSpeed ) { 10 this.maxSpeed = maxSpeed; 11 } 12 public String getBrand () { 13 return this.brand ; 14 } 15 public void setBrand (String brand ) { 16 this.brand = brand; 17 } 18 public double getPrice () { 19 return this.price ; 20 } 21 public void setPrice (double price ) { 22 this.price = price; 23 } 24 }
如果用FactoryBean的方式实现就灵活点,下例通过逗号分割符的方式一次性的为Car的所有属性指定配置值:
1 package com.baobaotao.factorybean; 2 import org.springframework.beans.factory.FactoryBean; 3 public class CarFactoryBean implements FactoryBean<Car> { 4 private String carInfo ; 5 public Car getObject() throws Exception{ 6 Car car = new Car() ; 7 String[] infos = carInfo.split(",") ; 8 car.setBrand (infos[0]) ; 9 car.setMaxSpeed (Integer.valueOf(infos[1])) ; 10 car.setPrice (Double. valueOf(infos[2])) ; 11 return car; 12 } 13 public Class<Car> getObjectType () { 14 return Car.class ; 15 } 16 public boolean isSingleton () { 17 return false ; 18 } 19 public String getCarInfo () { 20 return this.carInfo ; 21 } 22 23 // 接受逗号分割符设置属性信息 24 public void setCarInfo (String carInfo ) { 25 this.carInfo = carInfo; 26 } 27 }
有了这个CarFactoryBean后,就可以在配置文件中使用下面这种自定义的配置方式配置CarBean了:
<bean id="car"class="com.baobaotao.factorybean.CarFactoryBean" P:carInfo="法拉利,400,2000000"/>
当调用getBean("car")时,Spring通过反射机制发现CarFactoryBean实现了FactoryBean的接口,这时Spring容器就调用接口方法CarFactoryBean#getObject()方法返回。
如果希望获取CarFactoryBean的实例,则需要在使用getBean(beanName)方法时在beanName前显示的加上"&"前缀:如getBean("&car");