zoukankan      html  css  js  c++  java
  • 抽象工厂模式

    抽象工厂模式

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

  • 相关阅读:
    原码, 反码, 补码 详解
    位移运算符
    ASP.NET中httpmodules与httphandlers全解析
    MySQL count
    真正的能理解CSS中的line-height,height与line-height
    IfcEvent
    IfcWorkCalendarTypeEnum
    IfcSingleProjectInstance
    转换模型
    IfcTypeProduct
  • 原文地址:https://www.cnblogs.com/xiaojiesir/p/11064834.html
Copyright © 2011-2022 走看看