`

创建模式之工厂、工厂方法、抽象工厂与建造者

阅读更多

      序:上一篇博文关于模式中粗略的谈了下何谓模式、模式的要素、理解模式的核心关注点以及在java中使用模式常涉及到的抽象类与接口异同问题,在接下来的篇章里将陆续介绍GOF 23的模式。由于博文仅倾向于模式的理解与相似模式间关系,所以,博文可能会以某类模式一起论述的形式出现。这些主要是个人回顾性的总结,具有较强的随意性,必定存在论述上的不周或过于累赘,还望朋友们海涵指正。

      我们知道设计模式的最基本的原则是状态变化部分和不变部分尽可能地分离,比如桥接(Bridge)模式采用的是抽象和抽象的实现分离,这种分离想达到的效果就是较好的复用,就是对开-闭原则的最大限度支持。我们学习模式,为的就是理解模式,充分重用前辈经验,站在巨人的肩膀上,为设计出好的设计找寻方法。

      由GOF整理的23种设计模式共分三大类:创建模式、结构模式和行为模式。

      人们往往认为最为简单的就是创建模式,也许是因为日常工作中涉及到的机会更多些,尤其是工厂类模式,若有面试时一般情况下面试官会说除工厂类模式之外请你介绍下你较为熟悉的模式。不过,在创建模式中,单例(Singleton)模式却是常被拿来作为笔试考察的,侧重于单例模式的实现(所谓的饿汉、懒汉、注册、双重检查等实现方式优劣以及多类加载器、多JVM情况下Singleton的表现)及其使用场景。

      尽管创建模式被认为相对简单,但加深对它们的认识还是必要的。创建模式,它抽象了类的实例化过程,针对类,使用继承改变了真正被实例化的类,针对对象,则会将对象实例化委托给另一个对象。从该角度看,创建模式利用继承和引用的方式来完成需要的各种对象的创建工作,把和对象的创建相关的事务统一进行处理,并通过创建不同类别的对象来应对信息系统外部的变化,为系统提供灵活性。当系统的灵活性不是依赖于多态继承机制,并随着系统演化得越来越依赖于对象的复合,创建模式变得更为重要,甚至成为系统灵活机制的核心,原因就在于创建模式可以通过委托方式来满足这一需求。

      创建模式包含有简单工厂(SimpleFactory)模式、抽象工厂(AbstractFactory)模式、建造者(Builder)模式、单例(Singleton)模式和原型(Prototype)模式,以下篇幅的介绍对象是简单工厂模式、抽象工厂模式、建造者模式以及由工厂和抽象工厂引出的工厂方法模式,单例和原型将在以后篇幅给予阐述。

      #简单工厂(SimpleFactory)模式:又称静态工厂方法模式,是由一个工厂对象决定创建出哪一种产品的实例。下图是工厂模式的一般性示意图:

      从示意图中我们可以看出该模式的优点是客户端Client无需关心产品的创建,创建责任有核心的工厂类Factory负责;而缺点是核心的工厂类Factory必须知道要创建哪一类产品及如何被创建,尤其是当产品类有不同的接口种类时,核心的工厂类Factory需要判断何时生产何种商品,使得系统将来扩展比较困难。因此,该模式此时对“开-闭”原则的支持度为有限支持,即不影响客户端,但需要维护工厂自身逻辑。

      关于实现,我们首先看Product,它应该是抽象类呢还是接口呢?

      这个问题其实在关于模式中曾提到过的,并给出了个人认为较为合适的答案。答案的核心在于是否需要多继承及是否彼此间有相同的商业逻辑需要共享进行代码上移。一般情况下推荐使用接口(现代开发最佳实践面向接口编程),这里给出几种具体场景的个人认为该使用何种方式的答案。其一是可能需要多继承,此时,Product此时均应为接口,若有此种情况同时兼有商业逻辑需要共享,则遵循代码上移的原则,可再抽象出一层AbstractProduct实现Product接口完成此使命,若在关于模式中举的水果和蔬菜的例子,它们各自有共享商业逻辑代码的需要,同时又存在多继承的可能性(蔬菜的西红柿变身水果);其二是不存杂多继承,但需要共享商业逻辑代码,Product应为抽象类。

      其次看核心的工厂类的工厂方法,若该工厂只负责生产一种产品类型,借助java反射是完全可以做到完全支持“开-闭”原则的,即增加新产品不会影响原有系统功能,如下实现说明:

//不完全支持开-闭原则的实现,因为if...else模式使得新加入产品时需要维护Factory
public class Factory {
   public static Product factory(String which) {
       if(which.equalsIgnoreCase("product1") ){
           return new ConcreteProduct1();
        } else if(which.equalsIgnoreCase("product1")) {
           return new ConcreteProduct2();
        } else {
           throw new RuntimeException("the product type doesn't exist");
        }
    } 
}
//完全支持开-闭原则,因为java反射消除了新增产品对Factory的维护
public class Factory {
   public static Product factory(String productpath) {
       try{
          return (Product) Class.forName(productpath).newInstance();
       }catch(Exception e){
          throw new RuntimeException("the product type doesn't exist");
       }
   }
}

      若该工厂负责生产至少两种产品类型,则该简单工厂模式只能保持它对开-闭原则的不完全支持,因为,核心的工厂类Factory需要判断何时生产何种商品,不免会因产品的增加而需要维护工厂自身逻辑。对于这种产品族工厂生产需求,为了满足对开-闭原则的支持需要使用后面将要介绍的抽象工厂模式。

      这里稍微提一下针对抽象(接口)编程,针对抽象编程--利用具体产品类的超类类型,将它的真实类隐藏起来。其优点是提供了系统的可扩展性。(若将来有新的具体子类被加入到系统中来,那么工厂类可以将交给客户端的对象换成新的子类实例,而对客户端无任何影响)这种将工厂方法的返回类型设置成抽象产品类型的做法,叫做针对抽象编程,这是依赖倒转原则(DIP)的应用。

     最后,我们来看一下针对简单工厂方法在单产品和产品族下存在的更好的模式,即工厂方法和抽象工厂。

     #工厂方法(FactoryMethod):把对象的创建延迟到子类中进行,其核心是在父类和子类之间分配创建的责任,即把原先应该有父类直接完成的事情交给子类去做。

     工厂方法与产品存在层对应关系,一般的工厂方法模式示意图如下:

      由上面工厂方法的示意图我们可以看出工厂方法与简单工厂有如下几点关系:1、结构上明显不同。工厂方法的核心是一个抽象工厂类(XCreator),简单工厂模式把核心放在一个具体类上。2、工厂方法模式保持了简单工厂模式的优点,且客服了它的缺点。简单工厂模式中,一个工厂类处于产品类实例化的中心位置上,它知道每一个产品,决定哪一个产品应当被实例化,存在一定的优缺点;工厂方法模式是简单工厂模式的进一步抽象和推广。因使用了多态性,工厂方法模式保持了简单工厂模式的优点,且客服了它的缺点。工厂方法模式中核心的工厂类不再负责所有产品的创建,而是将具体的创建工作交给了子类去做,这个核心类则摇身变为了一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。这种进一步抽象的结果,使得工厂方法模式允许在不修改具体工厂角色的情况下引入新的产品,遵循“开-闭”(实指单产品下相比SimpleFactory的if...else实现模式)。

     为了更加直观的对该模式产生认识,给出以下示例性代码: 

//client invoke sample
public class Client {
   private XCreator xConcreteCreator1,xConcreteCreator2;
   private XProudct xConcreteProduct1,xConcreteProduct2;
   public static void main(String[] args) {
      xConcreteCreator1 = new XConcreteCreator1();
      xConcreteProduct1 = xConcreteCreator1 .factory();
      xConcreteCreator2 = new ConcreteCreator2();
      xConcreteProduct2 = xConcreteCreator2 .factory();
  }
}
//creator sample
public interface XCreator{
   public XProduct factory();
}
public class XConcreteCreator1 implements XCreator {
   public XProduct factory() {
      retrun new XConcreteProduct1();//maybe more complisity creating the product
   }
public class XConcreteCreator2 implements XCreator {
   public XProduct factory() {
      retrun new XConcreteProduct2();//maybe more complisity creating the product
   }
//product sample
public interface XProduct{}
public class XConcreteProduct1 implements XProduct {}
public class XConcreteProduct2 implements XProduct {}

        关于工厂方法的更好学习实例推荐参考java源码中迭代子实现,其结构示意图如下图左,工厂方法的一般性调度流程示意图如下图右:

 

       实际上,使用工厂方法FaintMethod的核心根源并不在于仅仅把对象的创建延迟到子类中,那只是处理方法。使用的原则是客户对象需要处理很多的某种子类对象,客户对象面临两个问题,一个是选择创建哪一个子类的对象,一个是被选定的子类对象如何人被建立。但是这两个逻辑,工厂方法模式把他们分开了。在模式之外选择建立哪一个子类对象,而在具体建立这个对象的时候,又使用工厂类的子类来屏蔽建立的这个子类对象的特殊性。这样,客户就可以独立于需要使用的子类对象的变化而始终保持稳定了。 理解工厂方法的时候,需要认识到工厂方法的子类仅仅是屏蔽了产品子类创建的不一致性,而具体选择哪一个产品则是外部逻辑提供的。工厂方法的价值在于,产品对象的建立和初始化的不同被屏蔽了,因此,如果子类对象的创建方法没有大的不同,那么使用工厂方法模式是无价值的。

      #抽象工厂(AbstractFactory )模式:抽象工厂引入产品簇的概念(工厂与产品存在层次结构)!该模式是所有形态的的工厂模式(简单工厂、工厂方法、抽象工厂)中最为抽象&最具一般性(如下图,左边代表工厂,右边代表不同产品形成的产品簇,而右图是一般性的实现)。该模式用意是向客户端提供一个接口,使得客户端在不必指定产品具体类型的情况下,创建多个产品簇中的产品对象。

     上图展示了由ProductA、ProductB为类别的两类产品,存在簇1{ProductA1,ProductB1}和簇2{ProductA2,ProductB2},并分别有工厂ConcreteCreator1和ConcreteCreator2负责创建,为了更直观展示给朋友们形成感性认识,下面给出示例性代码:

public interface Creator {
    public ProductA fatoryA();
    public ProductB factoryB();
}

public class ConcreteCreator1 implements Creator {
    public ProductA factoryA() {
           retrun new ProductA1();
    }
    public ProductB factoryB() {
           retrun new ProductB1();
    }
}

public class ConcreteCreator2 implements Creator {
    public ProductA factoryA() {
           retrun new ProductA2();
    }
    public ProductB factoryB() {
           retrun new ProductB2();
    }
}

public interface ProductA {}
public class ProductA1 implements ProductA {
    public ProductA1(){}
}
public class ProductA2implements ProductA {
    public ProductA2(){}
}

public interface ProductB {}
public class ProductB1 implements ProductB {
    public ProductB1(){}
}
public class ProductB2implements ProductB {
    public ProductB2(){}
}

        在上面阐述简单工厂(SimpleFactory)模式时曾提及对于产品族需求的工厂,简单工厂(SimpleFactory)模式无法很好的支持开-闭原则,那么,抽象工厂(AbstractFactory)模式对开-闭原则的支持度到底又如何呢?简单地说,是有选择的完全支持。在产品等级结构的数目不变的情况下,增加新的产品族,对原有系统不会造成任何影响,此时是完全支持开-闭原则。然而,在产品族的数目不变的情况下,加新的产品等级结构,则原有系统实现的FactoryMethod都应做相应的调整,此时是处于不支持开-闭原则状态的。

        关于何时应该考虑使用抽象工厂,前人总结了以下几点:1、一个系统不应当依赖于产品实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。2、系统拥有的产品有多余一个的产品族,而系统只消费其中某一族的产品。(与抽象工厂的起源有关)3、同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。4、系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。

       以上这几点在抽象工厂(AbstractFactory)模式的起源中有很好的体现,其实,抽象工厂起源于跨操作系统的产品实现,通过下图的抛砖引玉,相信大家已不言自明了:

抽象工厂(AbstractFactory)模式起源

      #建造者(Builder )模式:将一个产品的内部表象与产品的生产过程分离,从而可以使一个建造过程生成具有不同内部表象的产品对象。若我们把工厂模式看做现实世界的生产车间,负责某种产品的创建工作,那么我们可以把建造者模式看做是组装车间,负责产品组装形成相对复杂的产品。比如采用Builder模式来生成汽车,我们便可以用工厂方法生产汽车所需的各类部件(轮胎、车灯、方向盘、发动机等等),然后由Builder负责组装成最终产品汽车。下图是通用性的Builder模式示例图:

       这样以来,我们便可以看到简单工厂模式、工厂方法、抽象工厂模式与建造者模式它们之间的一些关系与侧重点。简单工厂模式负责产品的创建,使得客户端无需关心产品的创建过程,工厂方法是针对简单工厂模式存在的缺点(不完全支持开-闭原则,增加新产品虽不影响客户端,但需要维护简单工厂逻辑)而引入的,抽象工厂模式引入了产品族的概念,是工厂模式的进一步扩展,而建造者模式则更侧重于复杂产品的生产,期间可以利用工厂模式完成builderPart工作。

      以下是使用建造者模式几种场景以及期待达到的效果。

      场景:1、需要生成的产品对象有复杂的内部结构。(工厂各车间:每个内部成分本身可以是对象,也可以仅是产品对象的一部分,自身并不构成对象概念)
               2、需要生成的产品对象的属性相互依赖。(工厂流水线:builder模式可以强制实行一种分步骤进行的建造过程,因此,若产品对象的一个属性必须在另一个属性被赋值之后才可以赋值,使用builder模式便是一个很好的设计思想)
               3、在对象创建过程中会使用到系统中的其它一些对象,这些对象在产品的创建过程中不易得到。
      效果:使用builder模式主要有以下效果:
              1、builder模式的使用使得产品的内部表象可以独立地变化。客户端不必知道产品内部组成的细节。
              2、每一个builder都相对独立,与其它的builder无关。
              3、模式所建造的最终产品更易于控制。

      在这里我们着重看下建造者(Builder)模式与工厂(Factory)(主要是AbstractFactory)模式间的关系。两者很相似,都是用来创建同时属于几个产品族的对象的模式。不同之处在于--AbstractFactory模式中(相对简单产品),每一次工厂对象被调用时都会返回一个完整的对象,至于client如何处理该对象与工厂自身无关;Builder模式(相对复杂产品,侧重产品组装流水),它则是一点一点地建造出一个复杂的产品,而这个产品的组装过程就发生在builder角色内部。这两种模式,AbstractFactory更加微观,而Builder更加宏观。一个系统可以由一个bulider和一个AbstractFactory组成,client通过调用这个builder角色,间接地调用另一个AbstractFactory角色,AbstractFactory模式返还不同产品族的零件,而Builder模式则把他们组装起来。

     从某种角度来说,Build模式也是我们完成生产某种产品工作的策略,这不免会使我们想到策略(Strategy)模式。事实上,Builder模式是Strategy模式的一种特殊情况,而区别在于各自的目标不同。Builder模式适用于为client客户端一点一点地建造新的对象,而不同类型的具体Builder角色虽然都拥有相同的接口,但它们所创建出来的对象可能完全不同;Strategy模式目的是为算法提供抽象的接口,即利用里氏替换满足“开-闭”,通过具体策略类封装某算法,不同的策略类对象为一种一般性的服务提供不同的实现。

     与此同时,我们看到Builder组装产品的过程类似于模板(Template)模式,比如builder.builderPart1()-->builder.builderPart2()-->retrieveResult()这样一条流水生产产品,事实上,失去Director角色后的Builder模式顺理成章地发展到了Template模式。

     说到这里,大家可能已经意识到模式间是存在关系的,这里便涉及到如何区别与联系这些模式间关系的问题,在这里,我给出一些个人建议。 首先要看模式的类别,一般来说,结构模式和行为模式服务于创造模式,创造模式和服务于客户所需的最终产品。创建模式侧重于产品的创建方法,结构模式侧重于产品的结构间关系结构设计,行为模式侧重于出于创建产品的目的,在创作过程中表现出的行为。其次要看各模式的目的及使用场景与达到的效果,这个过程主要是考察重用粒度与对开-闭原则的支持度,在关于模式中我们已经提到过,保障重用原则的是代码上移&数据下移,保障开-闭原则的是依赖倒转、接口隔离、迪米特原则、里氏替换原则和组合/聚集,不再赘述。《暂完》   

1
0
分享到:
评论

相关推荐

    工厂模式(简单工厂,普通工厂,抽象工厂)代码java

    在软件设计模式中,工厂模式是一种非常...在实际项目中,工厂模式经常与其他设计模式(如策略模式、建造者模式等)结合使用,以解决更复杂的问题。阅读并理解这些代码有助于深入理解工厂模式及其在Java编程中的应用。

    建造者模式学习以及与工厂模式的区别

    建造者模式(Builder Pattern)是一种创建型设计模式,它提供了一种方法来分步骤构建复杂的对象,使得构建过程和表示分离。这种模式常用于当一个对象的构建过程复杂且需要多个步骤时,或者当我们希望同一个构建过程...

    day02 工厂模式+原型模式+建造者模式

    设计模式之工厂模式、原型模式和建造者模式 设计模式是一种软件设计的解决方案,能够使我们的代码更加灵活、可维护和可扩展。今天,我们将讨论三种设计模式:工厂模式、原型模式和建造者模式。 工厂模式 工厂模式...

    设计模式之建造者模式代码示例

    建造者模式(Builder Pattern)是软件工程中一种用于创建对象的抽象工厂方法,它将复杂的构建过程分解为一系列步骤,使得构建过程可以独立于其表示进行。这种模式允许客户端使用相同的构建过程创建不同表现形式的...

    单例模式,工厂模式,抽象工厂模式,建造者模式VS2019 C++代码实现

    主要实现了4种设计创建型模式的示例,分别是单例模模式(Singleton Pattern),工厂模式(Factory pattern),抽象工厂模式(Abstract Factory pattern),建造者模式(Builder Pattern) 使用人群:希望对这四种模式有所...

    Java建造者模式源码

    例如,可能会引入抽象工厂模式,让建造者能够创建不同类型的子产品。此外,有时建造者会带有参数,允许用户在构建过程中指定某些特性。还有时,建造者模式会被用来实现链式构建,即每个构建方法都返回Builder自身,...

    46丨建造者模式:详解构造函数、set方法、建造者模式三种对象创建方式1

    建造者模式与工厂模式的主要区别在于,工厂模式关注的是创建一个特定的产品实例,而建造者模式更专注于创建产品的步骤和组件。工厂模式通常用于创建简单对象,而建造者模式适用于创建复杂的、有多个组件的对象。在...

    研磨设计模式之工厂方法模式.

    1. 抽象工厂(Abstract Factory):这是工厂方法模式的接口,声明了创建产品的工厂方法。 2. 具体工厂(Concrete Factory):实现了抽象工厂的接口,提供具体的工厂方法来创建产品对象。 3. 抽象产品(Abstract ...

    建造者模式.pptx

    建造者模式是一种将“复杂对象的构建算法”与它的“部件及其组装方式”相分离的设计模式。这样做的目的是让构建算法能够独立于产品的部件变化,从而更好地适应需求的变化。该模式适用于以下几种情况: 1. 当创建复杂...

    设计模式之工厂模式详细

    无论是简单工厂、工厂方法还是抽象工厂,它们都在不同场景下提供了创建对象的优雅方式,是每个开发者都应该掌握的设计模式之一。在学习和实践中,理解每种模式的原理、优缺点以及适用场景,能够提升我们的编程素养,...

    设计模式之建造者模式

    BuilderFactory这个文件名可能是实现建造者模式的一个工厂类,它可能包含用于创建不同类型建造者对象的方法。在实际应用中,BuilderFactory可以根据参数或者配置来返回不同的具体建造者实例,以此来构建不同类型的...

    建造者模式1

    与工厂方法模式相比,建造者模式更加关注零件的组装过程,而不是零件的创建过程。实例从实例的 UML 图中可以看出,产品的组成是不变的,具体如何装配产品由具体的建造者决定。各具体建造者相互独立,如果需要添加新...

    设计模式之工厂系列

    4. **建造者模式(Builder)**:与传统的工厂模式不同,建造者模式关注的是复杂对象的构建过程,通过一步一步的组装步骤,最终得到完整的对象。它可以将构造过程和表示分离,使得同样的构造过程可以创建不同的表示。...

    设计模式-工厂模式

    工厂模式主要有三种形式:简单工厂模式、工厂方法模式和抽象工厂模式。 1. **简单工厂模式**: 简单工厂模式通常由一个静态工厂类来负责创建对象,这个静态工厂类根据传入的参数或条件来决定创建哪种类型的实例。...

    设计模式之创建型模式实验.docx

    在本实验中,我们将探讨五种常见的创建型设计模式:简单工厂模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式,并在Java环境中实现这些模式。 1. **简单工厂模式**: - **概念**:简单工厂模式通过一个...

    C# 设计模式-建造者模式

    建造者模式(Builder Pattern)是软件工程中一种用于创建对象的抽象工厂方法,它将复杂的对象构造过程分离开来,使得相同的构造过程可以创建不同的表示。这种模式在C#编程中广泛应用于创建对象的复杂性较高,或者...

    Java设计模式-建造者模式详解

    Java设计模式-建造者模式详解 Java设计模式-建造者模式详解将一个复杂对象的构建与它的表示...同时,Builder模式也可以与其他设计模式结合使用,例如工厂模式、抽象工厂模式等,以实现更加灵活和可控的对象构建过程。

    设计模式建造者模式PPT学习教案.pptx

    * Abstract Factory Pattern:将建造者模式与抽象工厂模式结合,使用抽象工厂来生成不同的产品。 九、总结 建造者模式是一种对象创建型模式,它可以将客户端与包含多个部件的复杂对象的创建过程分离,使得同样的...

    工厂方法模式

    工厂方法模式是面向对象设计模式中的行为模式之一,它提供了一种创建对象的最佳方式。在工厂方法模式中,一个工厂类负责创建对象,而具体的创建过程则由子类决定。这种模式符合“开闭原则”,即对扩展开放,对修改...

    工厂模式实例

    工厂模式是设计模式的基础,许多其他高级设计模式(如建造者模式、装饰器模式等)都建立在其之上。因此,无论是在企业级应用开发还是在个人项目中,熟悉并灵活运用工厂模式都是提升代码质量的关键。

Global site tag (gtag.js) - Google Analytics