23种设计模式(四)建造者(生成器)模式

本文介绍

学习建造者(生成器)模式(builder Pattern)

建造者模式

建造者模式,又名生成器模式,是一种软件设计模式,
属于创建型模式。建造者模式将一个复杂对象的构建与它的表示分离,
使得同样的构建过程可以创建不同的表示(属性)。

简介

建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节

主要角色

在这样的设计模式中,有以下几个角色:

1、builder(抽象建造者):为创建一个产品对象的各个部件指定抽象接口。

2、ConcreteBuilder(具体建造者):实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并提供一个检索产品的接口。

3、Director(指挥者):构造一个使用Builder接口的对象。 它主要是用于创建一个复杂的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程

4、Product(产品角色):表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程,包含定义组成部件的类,包括将这些部件装配成最终产品的接口。

基本的实现思路

优缺点

优点

1、不必知道产品内部组成的细节,产品的建造和表示分离,实现了解耦。

2、隐藏了产品的建造细节,用户只需关心产品的表示,而不需要了解是如何创建产品的。

3、每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象

4、可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程

5、体现了开闭原则,增加新的具体建造者无须修改原有建造者代码,指挥者类针对抽象建造者类编程,扩展方便

缺点

1、当建造者过多时,会产生很多类,难以维护。

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

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

适用场景

1、当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。 [1]

2、当构造过程必须允许被构造的对象有不同表示时。

注意事项

实现代码

1、一个典型的复杂对象其类代码示例如下:

package com.test.builder;
public class Product {
    /**
     * 可以是任意类型
     */
    private String partA;
    private String partB;
    private String partC;

    public String getPartA() {
        return partA;
    }

    public void setPartA(String partA) {
        this.partA = partA;
    }

    public String getPartB() {
        return partB;
    }

    public void setPartB(String partB) {
        this.partB = partB;
    }

    public String getPartC() {
        return partC;
    }

    public void setPartC(String partC) {
        this.partC = partC;
    }
}

2、抽象建造者类中定义了产品的创建方法和返回方法,其典型代码如下:

public abstract class Builder
{
    protected Product product=new Product();

    public abstract void buildPartA();
    public abstract void buildPartB();
    public abstract void buildPartC();

    public Product getResult()
    {
        return product;
    }
}

3、具体建造者。实现抽象接口,构建和装配各个部件,实例代码如下:

package com.test.builder;

public class ConcreteBuilder extends Builder{
    @Override
    public void buildPartA(){
    //something
    }

    @Override
    public void buildPartB(){
    //something
    }

    @Override
    public void buildPartC(){
    //something
    }
}

4、指挥者类的代码示例如下:
建造者模式的结构中还引入了一个指挥者类Director,该类的作用主要有两个:一方面它隔离了客户与生产过程;另一方面它负责控制产品的生成过程。指挥者针对抽象建造者编程,客户端只需要知道具体建造者的类型,即可通过指挥者类调用建造者的相关方法,返回一个完整的产品对象。

package com.test.builder;

/**
 * Director
 *
 * @author xxx
 */
public class Director {
    private Builder builder;

    /**
     * 1构造方法的方式注入builder对象
     * @param builder
     */
    public Director(Builder builder)
    {
        this.builder = builder;
    }

    /**
     * 2 set方法注入builder对象
     * @param builder
     */
    public void setBuilder(Builder builder)
    {
        this.builder = builder;
    }

    public Product construct()
    {
        builder.buildPartA();
        builder.buildPartB();
        builder.buildPartC();
        return builder.getResult();
    }
}

5、客户端类代码片段:

在客户端代码中,无须关心产品对象的具体组装过程,只需确定具体建造者的类型即可,建造者模式将复杂对象的构建与对象的表现分离开来,这样使得同样的构建过程可以创建出不同的表现。

package com.test.builder;

public class TestMain {
    //something

    Builder builder = new ConcreteBuilder();
    Director director = new Director(builder);
    Product product = director.construct();

    //something
}

例子

贩卖不同内容的纸

建造者模式可以用于描述创建不同内容的纸张
一张纸上的内容一般包含很多内容,按照头部,身体 脚等区分内容
不同的纸有不同的组成内容,可以根据顾客的要求,
一步一步装配这些组成部分,构造一份客户需要的内容的纸给他。

Product(产品角色)

具体的产品对象

package com.test.test;

public class Paper {
    private String head;
    private String body;
    private String hand;
    private String foot;
    public String getHead() {
        return head;
    }
    public void setHead(String head) {
        this.head = head;
    }
    public String getBody() {
        return body;
    }
    public void setBody(String body) {
        this.body = body;
    }
    public String getHand() {
        return hand;
    }
    public void setHand(String hand) {
        this.hand = hand;
    }
    public String getFoot() {
        return foot;
    }
    public void setFoot(String foot) {
        this.foot = foot;
    }
}

Builder(抽象建造者)

创建一个Product对象的各个部件指定的抽象接口。

package com.test.test;

public abstract class PaperBuilder {
    Paper paper = new Paper();

    public abstract void buildHead();

    public abstract void buildBody();

    public abstract void builHand();

    public abstract void buildFoot();

    public Paper getPaper(){
        return paper;
    }
}

ConcreteBuilder(具体建造者)

实现抽象接口,构建和装配各个部件

纸张A

package com.test.test;

public class PaperA extends PaperBuilder{

    @Override
    public void buildHead(){
        paper.setHead("A");
    }

    @Override
    public void buildBody(){
        paper.setBody("类型1");
    }

    @Override
    public void builHand(){
        paper.setHand("无手");
    }

    @Override
    public void buildFoot(){
        paper.setFoot("42码的脚");
    }
}

纸张B

package com.test.test;

public class PaperB extends PaperBuilder{

    @Override
    public void buildHead(){
        paper.setHead("B");
    }

    @Override
    public void buildBody(){
        paper.setBody("类型2");
    }

    @Override
    public void builHand(){
        paper.setHand("有手");
    }

    @Override
    public void buildFoot(){
        paper.setFoot("24码的脚");
    }
}

Director(指挥者)

构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象,它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程

package com.test.test;

public class PaperDirector {

    private PaperBuilder PaperBuilder;

    public PaperDirector(PaperBuilder PaperBuilder) {
        this.PaperBuilder = PaperBuilder;
    }


    public Paper construct(){
        //
        PaperBuilder.buildHead();
        //
        PaperBuilder.buildBody();
        //
        PaperBuilder.builHand();
        //
        PaperBuilder.buildFoot();

        //准备完毕,返回一个完整的纸给客户
        return PaperBuilder.getPaper();
    }
}

测试类(客户端类)

package com.test.test;

public class Test {
    public static void main(String[] args) {

        //纸A
        PaperA a = new PaperA();
        //准备纸A的管理者
        PaperDirector waiter = new PaperDirector(a);
        //获得套餐
        Paper PaperA = waiter.construct();
        System.out.print("纸A的组成部分:");
        System.out.println("头:"+PaperA.getHead()+";   "+"身体:"+PaperA.getBody()+";   " + "手:" + PaperA.getHand()+";   " + "脚:" + PaperA.getFoot());
    }
}

输出结果:

纸A的组成部分:头:A;   身体:类型1;   手:无手;   脚:42码的脚

总结

建造者模式的使用场合是当创建复杂对象时,把创建对象成员和装配方法分离出来,放在建造者类中去实现,用户使用该复杂对象时,不用理会它的创建和装配过程,只关心它的表示形式

参考文章

https://baike.baidu.com/item/%E5%BB%BA%E9%80%A0%E8%80%85%E6%A8%A1%E5%BC%8F/3229729
https://www.cnblogs.com/jenkinschan/p/6426694.html
https://blog.csdn.net/u010102390/article/details/80179754
https://www.cnblogs.com/snailclimb/p/builderpattern.html