抽象工厂模式

抽象工厂模式

抽象工厂模式(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");

posted @ 2019-06-22 22:24  xiaojiesir  阅读(208)  评论(0编辑  收藏  举报