设计模式解密(6) - 建造者模式(生成器模式)

大学生就业培训,高中生培训,在职人员转行培训,企业团训

1、简介

定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

分解一下定义:

  1、复杂对象的表示;
  2、复杂对象的构建过程;
  3、可分离的通用构建过程,也适用于其它复杂对象的表示;
  4、适用于一些基本部件不会变,而其组合经常变化的时候。

英文:Builder

类型:创建类模式

 2、原理及组成

:类图

大学生就业培训,高中生培训,在职人员转行培训,企业团训

四个要素

  产品类:一般是一个较为复杂的对象,也就是说创建对象的过程比较复杂,一般会有比较多的代码量。在本类图中,产品类是一个具体的类,而非抽象类。

      实际编程中,产品类可以是由一个抽象类与它的不同实现组成,也可以是由多个抽象类与他们的实现组成。

  抽象建造者:引入抽象建造者的目的,是为了将建造的具体过程交与它的子类来实现。这样更容易扩展。

        一般至少会有两个抽象方法,一个用来建造产品,一个是用来返回产品。

  建造者:实现抽象类的所有未实现的方法,具体来说一般是两项任务:组建产品;返回组建好的产品。

  导演类:负责调用适当的建造者来组建产品,导演类一般不与产品类发生依赖关系,与导演类直接交互的是建造者类。

      一般来说,导演类被用来封装程序中易变的部分。

 

其中最重要的两个部分

  1、一个部分是Builder接口,这里是定义了如何构建各个部件,也就是知道每个部件功能如何实现 
  2、另外一个部分是Director,Director是知道如何组合来构建产品,也就是说Director负责整体的构建算法,而且通常是分步骤地来执行,也就是说如何组装这些部件。

不管如何变化,建造模式都存在这么两个部分,一个部分是部件构造。另一个部分是整体构建的算法。 
再直白点说,建造模式的重心在于分离构建算法和具体的构造实现,从而使得构建算法可以重用。具体的构造实现可以很方便地扩展和切换,从而可以灵活地组合来构造出不同的产品对象。

 3、实例引入

背景:模拟生产各种笔(这里假设笔的零件生产是有顺序的:笔芯 -> 笔壳 -> 组装)

创建抽象产品类 -- 笔

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.designpattern.builder;
/**
 * 笔  -- 抽象产品类
*/
public abstract class Pen {
    /**笔芯**/
    private String cartridge;
    /**外壳**/
    private String shell;
     
    public String getCartridge() {
        return cartridge;
    }
    public void setCartridge(String cartridge) {
        this.cartridge = cartridge;
    }
    public String getShell() {
        return shell;
    }
    public void setShell(String shell) {
        this.shell = shell;
    }
}

创建抽象建造者(笔builder) 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.designpattern.builder;
/**
 * 抽象建造者  -- 笔builder
*/
public abstract interface PenBuilder {
    /**
     * 生产笔芯
     */
    abstract void buildCartridge();
    /**
     * 生产外壳
     */
    abstract void buildShell();
    /**
     * 组装笔
     */
    abstract Pen buildPen();
}

创建具体产品类(圆珠笔)

1
2
3
4
5
6
7
8
9
package com.designpattern.builder;
/**
 * 具体产品类 -- 圆珠笔
*/
public class BallpointPen extends Pen{
    public BallpointPen(){
        System.out.println("生产组装圆珠笔");
    }
}

创建具体产品类(画笔)

1
2
3
4
5
6
7
8
9
package com.designpattern.builder;
/**
 * 具体产品类 -- 画笔
*/
public class BrushPen extends Pen{
    public BrushPen(){
        System.out.println("生产组装画笔");
    }
}

创建建造者(具体)  -- 圆珠笔builder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.designpattern.builder.impl;
 
import com.designpattern.builder.BallpointPen;
import com.designpattern.builder.Pen;
import com.designpattern.builder.PenBuilder;
 
/**
 * 建造者(具体)  -- 圆珠笔builder
*/
public class BallpointPenBuilder implements PenBuilder{
     
    Pen pen;
    public BallpointPenBuilder(){
        pen = new BallpointPen();
    }
     
    @Override
    public void buildCartridge() {
        pen.setCartridge("圆珠笔笔芯");
    }
 
    @Override
    public void buildShell() {
        pen.setShell("圆珠笔外壳");
    }
 
    @Override
    public Pen buildPen() {
        return pen;
    }
 
}

创建建造者(具体)  -- 画笔builder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.designpattern.builder.impl;
 
import com.designpattern.builder.BrushPen;
import com.designpattern.builder.Pen;
import com.designpattern.builder.PenBuilder;
 
/**
 * 建造者(具体)  -- 画笔builder
*/
public class BrushPenBuilder implements PenBuilder{
     
    Pen pen;
    public BrushPenBuilder(){
        pen = new BrushPen();
    }
     
    @Override
    public void buildCartridge() {
        pen.setCartridge("画笔笔芯");
    }
 
    @Override
    public void buildShell() {
        pen.setShell("画笔外壳");
    }
 
    @Override
    public Pen buildPen() {
        return pen;
    }
 
}

创建导演类  Director

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.designpattern.builder;
/**
 * 导演类  Director
*/
public class PenDirector {
 
    public Pen constructPen(PenBuilder pen_builder){
        //生产笔芯
        pen_builder.buildCartridge();
        //生产外壳
        pen_builder.buildShell();
        //组装笔
        return pen_builder.buildPen();
    }
}

下面测试一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.designpattern.builder;
 
import com.designpattern.builder.impl.BallpointPenBuilder;
import com.designpattern.builder.impl.BrushPenBuilder;
 
/**
 * 测试
*/
public class Test {
    public static void main(String[] args) {
        PenDirector director = new PenDirector();
        Pen ballpointpen = director.constructPen(new BallpointPenBuilder());
        Pen brushpen = director.constructPen(new BrushPenBuilder());
    }
}

结果:

1
2
生产组装圆珠笔
生产组装画笔

4、解决的问题

笔的种类不止这几种,还有很多,使用建造者模式可以将一类产品的构建过程和细节进行封装(实现代码的复用)与产品的表示进行分离,即:使用同样的构建过程可以创建不同的产品;

在各个建造者类,只需定义生产零件方法,具体的生产顺序定义在导演类中;

5、优缺点

优点:

    封装性很好:使用建造者模式可以有效的封装变化,在使用建造者模式的场景中,一般产品类和建造者类是比较稳定的,因此,将主要的业务逻辑封装在导演类中对整体而言可以取得比较好的稳定性。

    扩展性很好:建造者模式很容易进行扩展。如果有新的需求,通过实现一个新的建造者类就可以完成,基本上不用修改之前已经测试通过的代码,因此也就不会对原有功能引入风险。

    可以有效控制细节风险:由于具体的建造者是独立的,因此可以对建造者过程逐步细化,而不对其他的模块产生任何影响。

缺点:

  建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。

  如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

6、应用场景

  建造者模式 --- 建造具有具体细节的产品

  1、需要生成的产品对象有复杂的内部结构,这些产品对象具备共性。

  2、需要生成的产品对象的属性相互依赖,建造者模式可以强迫生成顺序。

  ........

7、与其他模式对比

  我们可以看到,建造者模式与工厂模式是极为相似的,总体上,建造者模式仅仅只比工厂模式多了一个“导演类”的角色。在建造者模式的类图中,假如把这个导演类看做是最终调用的客户端,那么图中剩余的部分就可以看作是一个简单的工厂模式了。

  这里先回归一下定义:

      工厂模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类;

      建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示;   

  与工厂模式相比,建造者模式一般用来创建更为复杂的对象,因为对象的创建过程更为复杂,因此将对象的创建过程独立出来组成一个新的类——导演类。也就是说,工厂模式是将对象的全部创建过程封装在工厂类中,由工厂类向客户端提供最终的产品;而建造者模式中,建造者类一般只提供产品类中各个组件的建造,而将具体建造过程交付给导演类。由导演类负责将各个组件按照特定的规则组建为产品,然后将组建好的产品交付给客户端。

  工厂模式一般都是创建一个产品,注重的是把这个产品创建出来就行,只要创建出来,不关心这个产品的组成部分。

  建造者模式也是创建一个产品,但是不仅要把这个产品创建出来,还要关心这个产品的组成细节, 组成过程。

8、总结

  建造者模式最主要功能是基本方法的调用顺序安排,也就是这些基本方法已经实现了;

  而工厂方法则重点是创建,你要什么对象就创造一个对象出来,组装顺序则不是他关心的。

  PS:建造模式的关键是导演角色,这个角色掌握了零件对象的状态和产品的整体组装蓝图。没有了这个角色,建造模式就不是建造模式。当然,导演角色可以同时持有很多种蓝图,按照需要给出完全不同的组装结果。

 

  PS2:源码地址   https://github.com/JsonShare/DesignPattern/tree/master 

    

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力! 
如果标题被标记为转载,转载请注明原作者地址,详见引用 
版权声明,转载请注明出处:http://www.cnblogs.com/JsonShare

http://www.cnblogs.com/JsonShare/p/7133403.html