从封装变化的角度看设计模式——对象创建

封装转变之工具确立

在工具确立的历程中,经常会泛起的一个问题就是通过显示地指定一个类来确立工具,从而导致紧耦合。这是由于确立工具时指定类名将使你受特定实现的约束而不是特定接口的约束。这会使未来的转变加倍庞大。要制止这种情形,就应该间接地确立工具。

这种紧耦合的问题很大水平是由new关键字带来的,由于new的紧耦合泛起,使得紧耦合的类很难独立地被复用,由于它们之间是相互依赖的。而且紧耦合发生单块的系统,要改变或者删掉一个类,就必须要明了和改变其他许多类。这也是导致系统难以维护和移植的一个主要缘故原由。

以是可以通过“工具确立”模式绕开new,从而制止在工具确立(new)历程中所导致的紧耦合(依赖详细的类),以此支持工具确立的稳固。

那么若何制止new呢?举个例子!

	public void fun1(){
		//...
		Product p = new Product(); //改为:Product p = productFactory.createProduct();
		//...
	}

这样的方式就是通过一个工厂挪用一个方式来确立响应的产物,然则可能人人又会发生一个问题,这样操作虽然解决了Product的new操作,然则对于ProductFactory而言不是也需要通过new来发生吗?

对于这个问题,我想是许多人在接触到设计模式的时刻都市去思索的问题,既然ProductFactory照样要用到new,那工厂类另有存在的必要吗?这时,我们可以会想到两种解决方式,一是将createProdyct()方式写成静态方式,这样挪用的时刻自然不需要new了。二是通过注入的方式,好比在应用类当中通过setter或是组织方式传入一个工厂类的工具。

对于静态方式而言,简朴地说,纵然是使用静态方式,那Product p = ProductFactory.createProduct()这样依然是一种紧耦合的方式,由于工厂类无法替换,和直接new出产物区别不大。

对于注入方式,人人更多的是疑惑,既然可以传入一个工厂类工具,那为什么不直接传入响应的产物,不是更简朴直接吗?固然不是的,首先需要明了的是,工厂类的作用是作为一个笼子,这个笼子需要辅助我们束缚住 ‘未来的转变’ ,要知道一个产物的转变可能总是大于工厂的转变。在这种情形下,举出一个最简朴的例子,你在编码的历程中,可能会用到不只一个产物,那你就可能需要许多setter或者修改组织方式;然则若是这些产物都可以通过这个工厂来获取,是不是就相当于用笼子关住了转变,使其在一个局限中跳动。

在学习设计模式时,永远要记着的一句话就是“设计模式是用来教会我们若何应对未来可能的转变”。若是你能够确定自己的系统未来没有转变,那自然用不到设计模式;或者你的系统未来全是转变,那也用不到设计模式,设计模式做的就是隔离稳固与转变,若是没有稳固,那就用不到设计模式。

‘new’是一种硬编码,事实 ’硬‘ 在那里,同样一个简朴的理由,若是未来组织方式发生转变或者说组织参数增添(削减),而在源码中有许多地方都是通过new来获取实例工具,找到并修改源码将会是一项很大的事情。

在解决这样的 “工具确立” 问题中就有工厂方式、抽象工厂、原型模式和制作者模式等相关设计模式。

工厂方式(Factory Method)

  1. 意图

    界说一个用于确立工具的接口,让子类决议实例化哪一个类。FactoryMethod使得一个类的实例化延迟到其子类。

  2. 实例

    Factory Method相对于简朴工厂而言,完全遵照了“不改代码”的原则,然则其使用情形相比抽象工厂使用条件没有那么高,因此可以说是使用最多的确立型模式之一了。

    思量这样一个应用,它可以向用户显示多种文档,好比word、pdf、txt等等。在这个框架中,首先,想到的可能就是应用简朴工厂模式。

    public interface Document{
        public void open();
        public void close();
        public void save();
       // ......
    }
    public class PdfDocument implements Document{
        @Override
        public void open(){
            //open pdfDocument code
            System.out.println("open pdf!");
        }
    
        @Override
        public void close() {
            System.out.println("close pdf!");
        }
    
        @Override
        public void save() {
            System.out.println("save pdf!");
        }
       // ......
    }
    public class TxtDocument implements Document{
        //Txt实现代码,同PdfDocument
        ......
    }
    public class DocumentFactory{
        public Document createDocument(String type){
            if(type=="pdf"){
               
                return new PdfDocument();
            }else if(type=="txt"){
                return new TxtDocument();
            }else {
                return null;
            }
        }
    }
    
    //简朴工厂模式在客户类当中的挪用
    public class Client {
        public static void main(String[] args) {
            DocumentFactory factory
                = new DocumentFactory();
            Document pdfDocument 
     			= factory.createDocument("pdf");
            pdfDocument.open();
            pdfDocument.save();
            pdfDocument.close();
    
            Document txtDocument 
      			= factory.createDocument("txt");
            txtDocument.open();
            txtDocument.save();
            txtDocument.close();
        }
    }
    

    这样简朴工厂模式,在不思量未来新文档类型的情形下,确实是一种不错的实现方式。然则在后续的扩展历程当中,若是需要增添新的文档类,就需要去修改DocumentFactory中的createDocument()方式,增添新的种别,而且客户还必须知道这些种别才气使用。

    为了应对这种情形,就泛起了工厂方式。工厂方式就直接将工厂抽象出来,每个产物对应一个工厂,消除工厂模式中的条件分支结构(实在另有一种消除条件语句的模式,就是之前“组件协作”当中的计谋模式)。

    //Document部门稳定
    public interface Document{
        public void open();
        public void close();
        public void save();
        ......
    }
    public class PdfDocument implements Document{
        public void open(){
            //open pdfDocument code
        }
        // close 和 save
        ......
    }
    public class TxtDocument implements Document{
        //Txt实现代码
        ......
    }
    //而且后续可以扩展新的文档类
    ......
    
    //修改factory部门如下
    public interface DocumentFactory{
        public Document createDocument();
    }
    public class PdfDocumentFactory 
        		implements DocumentFactory {
        @Override
        public Document createDocument() {
            return new PdfDocument();
        }
    }
    public class TxtDocumentFactory 
        		implements DocumentFactory {
        @Override
        public Document createDocument() {
            return new TxtDocument();
        }
    }
    //若是后续有新的产物,直接再实现DocumentFactory,获得新的工厂
    ......
    
    //挪用历程可做如下修改:
    public class Client {
        public static void main(String[] args) {
            //行使多态性子,直接天生响应的factory子类
            //消除了控制耦合
            DocumentFactory factory = new PdfDocumentFactory();
            Document pdfDocument
                    = factory.createDocument();
            pdfDocument.open();
            pdfDocument.save();
            pdfDocument.close();
    
            factory = new TxtDocumentFactory();
            Document txtDocument
                    = factory.createDocument();
            txtDocument.open();
            txtDocument.save();
            txtDocument.close();
        }
    }
    

    有人可能会有疑问,这样不是还没完全消除new吗?首先这里的客户类已经到最高的挪用条理了,这个历程当中是一定会有new的泛起,否则怎样举行程序挪用呢?

    我们所说的消除new的历程是指main与factory之间,发生的一个中心条理(如下面的App)中去消除new。

    //这样的代码中,就消除了new的存在
    //详细的注入历程可以由其他的形式完成,好比Spring中的DI
    public class App{
       private DocumentFactory factory;
        public void setFactory(DocumentFactory factory) {
            this.factory = factory;
        }
        public void operateDoc(){
           Document document = factory.createDocument();
           document.open();
           document.save();
           document.close();
       }
    }
    //main中的代码是最高条理,也是转变最频仍的条理,这里是不能能消除new的
    public class Client {
        public static void main(String[] args) {
            DocumentFactory factory = new PdfDocumentFactory();
            App app = new App();
            app.setFactory(factory);
            app.operateDoc();
            //同样对于其他的工厂类也是可以接纳同样的方式挪用。
            ......
        }
    }
    

    这样修改代码的利益在那里呢?第一,显而易见的就是完全实现了“开闭原则”的头脑,扩展时不再需要去修改源码。第二,有些工具的确立历程可能比较庞大,因此若是直接在应用程序当中使用new或者其他形式确立很贫苦,通过工厂确立之后,就不再需要去关注那些庞大的确立历程。第三,通过new确立,始终是一种硬编码的形式,若是在应用程序当中过多的使用这种方式,那么一旦某工具的确立方式发生改变,修改源码一定是很繁琐的。

  3. 结构——类确立型模式

    从封装变化的角度看设计模式——对象创建

  4. 介入者

    • Product(Document)

    界说工厂方式中工厂确立的工具的接口。

    • ConcreteProduct(PdfDocument、TxtDocument)

      实现Product的接口。

    • Creator(DocumentFactory)

      声明工厂方式——createProduct(),可以挪用该方式返回一个Product类型的工具。

    • ConcreteCreator(PdfDocumentFactory、TxtDocumentFactory)

      重界说工厂方式以返回详细的ConcreteProduct。

    • Client(客户类)

      使用工厂和产物,工厂方式模式中,客户类也是一个主要的介入者,由于工厂方式主要的作用就是分脱离客户类与产物类之间的耦合关系,以是脱离客户类去谈工厂方式模式时,总会以为差了些什么东西,无法完全体会到工厂方式模式的优势。

  5. 适用性

    在下列情形下可以使用Factory Method模式:

    • 当一个类不知道它所必须确立的工具的类的时刻。
    • 当一个类希望由它的子类来指定它所确立的工具的时刻。
    • 当类将确立工具的职责委托给多个辅助子类中的某一个,而且你希望将哪一个辅助子类是署理者这一信息局部化的时刻。

    简朴地说,就是使用历程中只需要声明一个抽象工厂类的引用,详细挪用谁人工厂去天生谁人工具,是由挪用者去确定的。

  6. 相关模式

    Abstract Factory经常用工厂方式来实现,抽象工厂确立产物的历程就可以使用工厂方式来完成。

    工厂方式通常在Template Method中被挪用,这一点在“组件协作”当中也提到过。

  7. 思索

    • Creator的两种实现情形。第一种情形,Creator只作为抽象层,也就是只声明接口,不做任何的实现;这种情形就必须要子类来实现。第二种情形,Creator作为一个详细的实现类,而不是抽象类,这种情形下,Creator可以提供一个缺省的实现接口,这样纵然没有子类重写它,客户可以通过这样一个缺省的实现完成任务。
    • 灵活运用工厂方式模式。作为一种确立类模式,在任何需要天生庞大工具的地方,都可以使用工厂方式模式。有一点需要注重的地方就是庞大工具适合使用工厂方式模式,而简朴工具,特别是只需要通过 new 就可以完成确立的工具(这也是为什么工厂方式解说用到的例子总是无法说服人的缘故原由之一),无需使用工厂方式模式;由于使用工厂方式模式,就需要引入一个工厂类,会增添系统的庞大度。

抽象工厂(Abstract Factory)

  1. 意图

    提供一个确立一系列相关或相互依赖工具的接口,而无需指定他们详细的类。

  2. 实例

    假定存在这样一个服务层,该层当中需要做的就是接见数据库中的数据,而且执行一系列的相关操作。凭据面向接口编程的头脑,可以先作这样一个代码编写。

    //对数据库举行接见的三个接口
    //先确立毗邻,再执行相关操作,最后返回响应效果
    public interface DBConnection{}
    public interface DBCommand{}
    public interface DBDataReader{}
    
    //对于MySql,可以确立以下实现
    public class MySqlDBConnection implements DBConnection{}
    public class MySqlDBCommand implements DBCommand{}
    public class MySqlDBDataReader implements DBDataReader{}
    //同样对于Sql Server,Oricle也是这样的实现
    ......
    

    这样的实现下,我们可以说是知足了面向接口编程的一个头脑;而且在实现中,我们可以为每个接口,根据工厂方式模式,为其确立一个工厂。

    //工厂接口
    public interface DBConnectionFactory{
        public DBConnection createDBConnetion();
    }
    public interface DBCommandFactory{
         public DBCommand createDBCommand();
    }
    public interface DBDataReaderFactory{
        public DBDataReader createDBDataReader();
    }
    //然后对于每个详细的数据库,实现差异的详细工厂
    //以MySql为例
    public class MySqlDBConnetionFactory implements DBConnectionFactory {
        @Override
        public DBConnection createDBConnetion() {
            return new MySqlDBConnection();
        }
    }
    public class MySqlDBCommandFactory implements DBCommandFactory {
        @Override
        public DBDBCommand createDBCommand() {
            return new MySqlDBCommand();
        }
    }
    public class MySqlDataReaderFactory implements DataReaderFactory {
        @Override
        public DBDataReader createDataReader() {
            return new MySqlDataReader();
        }
    }
    //剩下的Orcle,Sql Server也是云云
    ......
    

    工厂模式方式的挪用就不再演示,区别和工厂方式中的Document例子中差异不大。

    对于这样的实现,虽然我们很好的行使了工厂方式模式,然则也引入了工厂方式模式的一个坏处——大量的工具和类(本例当中,三个系列,每个系列三个产物,光产物就是9个子类;每个产物再对应一个工厂,一共就是18个子类)。在使用的历程中,反而能够显著的感觉到系统庞大度不减反增。而且,DBConnectionDBCommandDBDataReader显著是有着一定的关系的,换句话说,MySql确立的DBConnection是和MySqlDBCommand、MySqlDBDataReader一起使用的,若是泛起MySqlDBConnection、OricleDBCommand、SqlServerDBDataReader这种组合一定是无法正常执行的。这时抽象工厂的泛起,就很好的解决了这样的问题。

    //首先,详细的产物类不会发生转变,简化的主要是工厂条理
    //先抽象出抽象工厂,将产物系列的确立方式合并到一个接口中
    public interface DBFactory{
        public DBConnection createDBConnetion();
        public DBCommand createDBCommand();
        public DBDataReader createDBDataReader();
    }
    //凭据差异的详细工厂,确立详细的工具
    public class MySqlDBFactory implements DBFactory {
        @Override
        public DBConnection createDBConnetion() {
            
            return new MySqlDBConnection();
        }
    
        @Override
        public DBCommand createDBCommand() {
            return new MySqlDBCommand();
        }
    
        @Override
        public DBDataReader createDBDataReader() {
            return new MySqlDBDataReader();
        }
    }
    //Oricle,sql server的工厂,同样云云
    ......
    

    抽象工厂主要是对工厂条理的简化,这样修改下来,对比工厂方式模式,削减了2/3的工厂子类确立,只需要3个工厂(有多少个产物系列就有多少个工厂子类)就可以完成产物的确立。

    这样的一种确立工厂方式,不仅削减了工厂的数目,而且使得产物的一致性得以保证,它可以保证,一次只能使用同一个系列当中的工具。

    public class Client {
        public static void main(String[] args) {
            DBFactory factory = new MySqlDBFactory();
            App app = new App();
            app.setFactory(factory);
            app.operate();
            //同样对于其他的工厂类也是可以接纳同样的方式挪用。
            // ......
        }
    }
    class App{
        private DBFactory factory;
        public void setFactory(DBFactory factory) {
            this.factory = factory;
        }
        public void operate(){
            DBConnection connection
                    = factory.createDBConnetion();
            DBCommand command 
                	= factory.createDBCommand();
            DBDataReader reader 
                	= factory.createDBDataReader();
            //执行相关操作
            .....
        }
    }
    

    这样的应用程序代码,在一定水平上就削减了工厂子类的数目,而且在operate()中保证了产物系列的一致性,使得MysqlDBFactory天生的产物,只会是与MySql相关的。

  3. 结构——工具确立型模式

    从封装变化的角度看设计模式——对象创建

  4. 介入者

    • AbstractFactory(DBFactory)

      声明一个确立抽象产物工具的操作接口。

    • ConcreteFactory(MySqlDBFactory)

      实现确立详细产物工具的操作。

    • AbstractProduct(DBConnection、DBCommand、DBDataReader)

      为一类产物工具声明一个接口。

    • ConcreteProduct(MySqlDBConection、MySqlDBCommand、MySqlDBDataReader)

      Flask-Limit使用详细说明

      界说一个将被响应的详细工厂确立的产物工具,并实现抽象产物的响应接口。

    • Client

      挪用抽象工厂和抽象产物提供的接口。在确立者模式当中,客户类也是主要的介入成员,由于对确立模式的明了容易杂乱的点正是在客户类中的挪用历程 (new) 发生的,关于这个问题,已经在前面做过许多注释了,不再多说。

  5. 适用性

    以下情形可以使用AbstractFactory模式:

    • 一个系统要独立于它的产物的确立、组合和示意时。
    • 一个系统要由多个产物系列中的一个来设置时。
    • 当你要强调一系列相关的产物工具的设计以便举行团结使用时。
    • 当你提供一个产物类库,而只想显示它们的接口而非实现时。
  6. 相关模式

    Singleton:一个详细的工厂通常会是一个单件。由于在一个应用中,一样平常每个产物系列只需要一个详细工厂。

    Factory Method:在Abstract Factory中,仅仅是声明一个确立工具的接口,真正的确立历程是由详细工厂实现的。这时,可以为每一个方式对应的详细工具之间再界说一个工厂方式。但其问题就在于,这样的做法就像是在工厂方式上再套上一层抽象工厂,从而又增添了系统的庞大度。

  7. 思索

    • 难以支持新种类的产物 对于工厂模式而言,它的最大优点就在于保证了产物的一致性,但也正是云云,这就使得它的产物系列需要保持稳固,若是在后续的历程中泛起新的产物,好比在实例当中需要增添一个新的功效系列,就需要去扩展DBFactory接口,而且涉及到DBFactory及其子类的改变。
    • 界说可扩展的工厂 这是对于新种类产物确立灵活性的提高,然则不太平安;就是给确立 工具的操作增添一个参数,该参数来指定将被确立的工具种类。使用这种方式,抽象工厂就只需要一个“Create”方式和一个标识符参数即可完成确立操作。但问题就在于,客户需要完全领会所有的参数才气更好使用工厂确立自己所需要的工具。

原型模式(Prototype)

  1. 意图

    用原型实例指定工具的确立种类,而且通过拷贝这些原型确立新的工具。

  2. 实例

    Prototype模式,有点像是对工厂方式模式中产物与工厂的合并。怎么说呢?看下面的代码:

    //工厂方式模式中的产物类与工厂方式类
    public interface Document{
        public void open();
        public void close();
        public void save();
        ......
    }
    public interface DocumentFactory{
        public Document createDocument();
    }
    

    这是在Factory Method中使用的确立方式,而原型做的事就是,不再用工厂来举行确立,而是转而克隆的方式。酿成下面这样:

    //合并Document和DocumentFactory
    public abstract class Document 
        			implements Cloneable{
        public void open();
        public void close();
        public void save();
        ......
        //相当于Factory中的createDocument();
       public Object clone() {
          Object clone = null;
          try {
             clone = super.clone();
          } catch (CloneNotSupportedException e) {
             e.printStackTrace();
          }
          return clone;
       }
    }
    public class PdfDocument implements Document{
        @Override
        public void open(){
            //open pdfDocument code
            System.out.println("open pdf!");
        }
        @Override
        public void close() {
            System.out.println("close pdf!");
        }
        @Override
        public void save() {
            System.out.println("save pdf!");
        }
        //......
    }
    //文档类的实现与工厂方式中的一样
    ......
    

    那么在详细的客户类当中就是通过这样一种方式来举行挪用:

    public class App{
        //在工厂方式模式当中,这里是documentFactory
    	private Document prototype;
        public void setDoucument(Document document){
            prototype = document;
        }
        public void useDocument(){
            //documentFactory.createDocument();
            Document doc = prototype.clone();
            //然后使用prototype克隆出来的doc举行操作
            doc.open();
            ......
        }
    }
    //在客户类中挪用显示
    public class Client {
        public static void main(String[] args) {
            Document doc = new PdfDocument();
            App app = new App();
            app.setFactory(doc);
            app.useDocument();
            //同样对于其他的工厂类也是可以接纳同样的方式挪用。
            //......
        }
    }
    

    问题来了,为什么不直接用原型(prototype),而是要多一步克隆?解决这个问题,首先要明了的是,我们使用原型的目的不是将原型作为客户类的一个属性去使用,而是一个确立者。既然是一个确立者,那么在使用的历程中,就不只一个地方会用到同样类型的工具;若是在差异的地方都直接使用原型,可能会在某个地方修改了原型的值,从而使得其他直接使用原型的方式泛起不能预知的错误。

  3. 结构——工具确立型模式

    从封装变化的角度看设计模式——对象创建

  4. 介入者

    • Prototype(Document)

      声明一个克隆自身的接口。

    • ConcretePrototype(PdfDocument…)

      继续Prototype并实现克隆自身的接口。

    • Client

      让一个原型克隆自身从而确立一个新的工具。

  5. 适用性

    • 当一个系统应该独立于它的产物确立、组成和示意时,可以使用Prototype模式。

    • 当要实例化的类是在运行时刻指准时,好比动态装载。

    • 为了制止确立一个产物类平行的工厂类条理时。

    • 当一个类的实例只能有几种差异状态组合中的一种时,确立响应数目的原型并克隆他们可以比每次用合适的状态手工实例化该类更利便一些。

  6. 相关模式

    Abstract Factory和Prototype在某种方面是相互竞争的,然则在某种情形下也是可以一起使用,好比,在抽象工厂中存储一个被克隆的产物聚集,在使用时,直接凭据聚集中的工具返回响应的产物。

    大量使用Composite(组合模式)和Decorator(装饰器模式)的设计上也可以接纳Protorype来削减Composite或Decorator工具的确立。

  7. 思索

    • 削减了子类的组织。实例当中就可以看出,原型模式简化了工厂方式模式中的工厂类,因此,削减了许多子类的确立。
    • 资源优化。类初始化可能需要消化异常多的资源,这个资源包罗数据、硬件资源等等,使用原型模式就可以削减这样一个初始化历程。
    • 深、浅拷贝的问题。对于java而言,浅拷贝实现 Cloneable,深拷贝是通过实现 Serializable 读取二进制流。

制作者模式( Builder)

  1. 意图

    将一个庞大工具的构建与它的示意星散,使得同样的构建历程可以有差异的示意。

  2. 实例

    在游戏场景当中,尤其是3d场景中,必不能少就是建筑物,好比说屋子。对屋子的构建一定不是一下所有构建完成的,而是会分成几个部门,好比墙、窗户、地板、房顶、门,一部门、一部门地去构建。

    public abstract class House{
        //屋子属性,纹理、材质...
        private Texture texture;
        private Material material;
        ......
        //墙、窗户、地板、房顶、门
        public Wall buildWall();
        public Window buildWindow();
        public Floor buildFloor();
        public Door buildDoor();
        public Roof buildRoof();
        //屋子构建历程
        public void buildHouse(){
            buildFloor();
            Wall[] walls = new Wall[4];
            for(int i=0;i<walls.length;i++)
                walls[i] = buildWall();
            Window window = buildWindow();
            wall[1].setWindow(window);
            Door door = builDoor();
            wall[2].setDoor(door);
            buildRoof();
        }
    }
    

    这种构建方式,接纳的显著就是模板方式(Template Method),这种实现方式还可以凭据差异的屋子类型,实现详细的细节。

    //石头屋
    public class StoneHouse extends House{
        //详细实现细节
        .......
    }
    //茅草屋
    public class ThatchedHouse extends House{
        //详细实现细节
        .......
    }
    
    //根据模板主方式在客户类中的挪用形式显示:
    public class Client{
        public static void main(String[] args){
            //只需要天生一个响应工具即可在游戏场景中完成响应类型屋子的确立
            House house = new StoneHouse();
            house.buildHouse();	//天生石头屋
            
            house = new ThatchedHouse();
            house.buildHouse();	//天生茅草屋
            
        }
    }
    

    这种实现有什么问题呢?类太臃肿了,对吧~这样的实现历程,可以体现复用的头脑,然则问题之一就在于所有的内容所有都放在了一个类中,体现不出单一职责和类的信息与行为集中;这时就可以将确立历程星散出来,形成一个Builder,由这样一个Builder来专门卖力确立。

    //Director
    public class House{
        //屋子属性,纹理、材质...
        private Texture texture;
        private Material material;
        ......
        //增添builder的引入
        private HouseBuilder houseBuilder;
        public void setHouseBuilder(HouseBuilder hBuilder){
            houseBuilder = hbuilder;
        }
        //屋子构建历程
        public void buildHouse(){
            houseBuilder.buildFloor();
            Wall[] walls = new Wall[4];
            for(int i=0;i<walls.length;i++)
                walls[i] = houseBuilder.buildWall();
            Window window 
                = houseBuilder.buildWindow();
            wall[1].setWindow(window);
            Door door = houseBuilder.builDoor();
            wall[2].setDoor(door);
            houseBuilder.buildRoof();
        }
    }
    
    //星散出来的builder
    public interface HouseBuilder{
        //墙、窗户、地板、房顶、门
        public Wall buildWall();
        public Window buildWindow();
        public Floor buildFloor();
        public Door buildDoor();
        public Roof buildRoof();
    }
    public class StoneHouseBuilder
        			implements HouseBuilder{
        //详细实现细节
        .......
    }
    public class ThatchedHouseBuilder 
        			implements HouseBuilder{
        //详细实现细节
        .......
    }
    
    //修改事后,在客户类中的挪用形式显示:
    public class Client{
        public static void main(String[] args){
            //只需要天生一个响应工具即可在游戏场景中完成响应类型屋子的确立
            House house = new House();
            HouseBuilder builder 
                	= new StoneHouseBuilder();
            house.setHouseBuilder(builder);
            house.buildHouse();
            
            builder = new ThatchedHouseBuilder();
            //这个set历程可以运行时完成
            house.setHouseBuilder(builder);
            house.buildHouse();
        }
    }
    

    通过这样一种方式,实现庞大工具构建与示意的星散,而且,对于差异的屋子工具,若是屋子其他参数没有任何差异,就只需要传入响应的builder即可,而不需要再天生林林总总的子类(如StoneHouse、ThatchedHouse)。

    一旦天生工具,只需要修改其builder就可以马上改变其工具示意,而不需要新天生工具。而且这种修改历程,是可以动态完成的,就若是Spring当中的依赖注入历程一样,可以在运行时刻完成,而纷歧定是一开始就确定的

  3. 结构——工具确立型模式

    从封装变化的角度看设计模式——对象创建

  4. 介入者

    • Builder(HouseBuilder)

      为确立一个Product工具的各个部件指定抽象接口。

    • ConcreteBuilder(StoneHouseBuilder、ThatchedHouseBuilder)

      • 实现Builder的接口以组织各装配该产物的各个部件。
      • 界说并明确它所确立的示意。
    • Director(House)

      组织一个使用builder的工具。

    • Product(Wall、Window…)

      包含了界说组成部件的类以及被组织的庞大工具等。

  5. 适用性

    Builder的适用情形:

    • 当确立庞大工具的算法应该独立于该工具的组成部门,以及它们的装配方式时。
    • 当组织历程必须允许被组织的工具的差异的示意时。

    Builder模式更多的是体现的一种头脑,而不是详细的历程,这种头脑就是,当一个类的信息与行为过于臃肿时,也许可以接纳Builder这种方式对类的信息与行为举行重新划分,从而使得类看起来加倍的“轻” 。

  6. 相关模式

    Abstract Factory和Builder都是对庞大工具的确立封装,但二者的区别在于,Builder着重于一步一步构建一个庞大工具,而Abstract Factory着重于多个系列产物工具的确立,而且系列工具之间有着某种联系。

    Composite模式中工具通常情形下就是用Builder天生的。

  7. 思索

    • 可以改变产物的内部示意 Builder工具提供给Director一个组织产物工具的内部接口,从而使得产物的示意和内部结构得以隐藏。因此,在构建历程中,只需要替换差异的Builder就可以获得差异的示意。
    • Builder中的方式可以缺省为空 这样在详细的实现历程中,客户可以只实现他们感兴趣的操作,这种方式实在在许多模式中都可以应用,其最主要的头脑就是能用默认设置固然是最好的。

民众号:良许Linux

从封装变化的角度看设计模式——对象创建

有收获?希望老铁们来个三连击,给更多的人看到这篇文章

原创文章,作者:dddof新闻网,如若转载,请注明出处:https://www.dddof.com/archives/28056.html