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

建造者模式【Builder Pattern 】
在模板方法模式时用到了公司生产马车的例子。这里继续,客户有了新的要求,就是能够控制基本方法的执行顺序。
先看一下类图:

这里写图片描述

定义一个车辆模型的抽象类,所有的车辆模型都继承这个类

public abstract class CarModel {
    //这个参数是各个基本方法执行的顺序
    private ArrayList<String> sequence = new ArrayList<String>();
    /*
    * 模型是启动开始跑了
    */
    protected abstract void start();
    //能发动,那还要能停下来,那才是真本事
    protected abstract void stop();
    //喇叭会出声音,是滴滴叫,还是哔哔叫
    protected abstract void alarm();
    //引擎会轰隆隆的响,不响那是假的
    protected abstract void engineBoom();
    //那模型应该会跑吧,别管是人推的,还是电力驱动,总之要会跑
    final public void run() {
    //循环一遍,谁在前,就先执行谁
    for(int i=0;i<this.sequence.size();i++){
        String actionName = this.sequence.get(i);
        if(actionName.equalsIgnoreCase("start")){ //如果是start关键字,
        this.start(); //开启汽车
        }else if(actionName.equalsIgnoreCase("stop")){ //如果是stop关键字
        this.stop(); //停止汽车
        }else if(actionName.equalsIgnoreCase("alarm")){ //如果是alarm关键字
        this.alarm(); //喇叭开始叫了
        }else if(actionName.equalsIgnoreCase("engine boom")){ //如果是engine
        boom关键字
        this.engineBoom(); //引擎开始轰鸣
    }
}
    //把传递过来的值传递到类内
    final public void setSequence(ArrayList<String> sequence){
        this.sequence = sequence;
    }
}

然后定义要生产的奔驰、宝马类。

最后在主类设置执行的顺序,进行调用:

    BenzModel benz = new BenzModel();
    ArrayList<String> sequence = new ArrayList<String>(); //存放run的顺序
    sequence.add("engine boom"); //客户要求,run的时候时候先发动引擎
    sequence.add("start"); //启动起来
    sequence.add("stop"); //开了一段就停下来
    //然后我们把这个顺序给奔驰车:
    benz.setSequence(sequence);
    benz.run();

其中 setSequence 方法是允许客户自己设置一个顺序,是要先跑起来在有引擎声音还是先有引擎声音再跑起来,还是说那个喇叭就不要响,对于一个具体的模型永远都固定的。这样的设计体现不出程序的扩展性。

接下来客户可能提各种各种的需求,不同种类的车型可以有不同的执行顺序,有的可以不执行。于是乎,先看一下下面的类图设计:
这里写图片描述

增加了一个 CarBuilder 的抽象类,以及两个实现类,其目的是你要什么排列顺序的车,我就给你什么顺序的车,那我们先看 CarBuilder的定义:

public abstract class CarBuilder {
    //建造一个模型,你要给我一个顺序要,就是组装顺序
    public abstract void setSequence(ArrayList<String> sequence);
    //设置完毕顺序后,就可以直接拿到这个车辆模型

    public abstract CarModel getCarModel();
}

实现类可以定义如下:

public class BenzBuilder extends CarBuilder {
    private BenzModel benz = new BenzModel();

    public CarModel getCarModel() {
    return this.benz;
    }

    public void setSequence(ArrayList<String> sequence) {
    this.benz.setSequence(sequence);
    }
}

在主类中进行调用:

    ArrayList<String> sequence = new ArrayList<String>(); //存放run的顺序
    sequence.add("engine boom"); //客户要求,run的时候时候先发动引擎
    sequence.add("start"); //启动起来
    sequence.add("stop"); //开了一段就停下来
    //要一个奔驰车:
    BenzBuilder benzBuilder = new BenzBuilder();
    //把顺序给这个builder类,制造出这样一个车出来
    benzBuilder.setSequence(sequence);
    //制造出一个奔驰车
    BenzModel benz = (BenzModel)benzBuilder.getCarModel();
    //奔驰车跑一下看看
    benz.run();

这样便可以针对不同的车子的不同需求进行不同的设计建造。

但是如果要有不同的组合呢?类似于有一个导演指挥这演员的演出顺序等。那我们设计一个导演类来扩展一下。先看类图:

这里写图片描述

增加了Director类:代码实现

public class Director {
    private ArrayList<String> sequence = new ArrayList();
    private BenzBuilder benzBuilder = new BenzBuilder();
    private BMWBuilder bmwBuilder = new BMWBuilder();
/*
* A类型的奔驰车模型,先start,然后stop,其他什么引擎了,喇叭一概没有
*/
public BenzModel getABenzModel(){
    //清理场景,这里是一些初级程序员不注意的地方
    this.sequence.clear();
    //这只ABenzModel的执行顺序
    this.sequence.add("start");
    this.sequence.add("stop");
    //按照顺序返回一个奔驰车
    this.benzBuilder.setSequence(this.sequence);
    return (BenzModel)this.benzBuilder.getCarModel();
    }
    /*
    * B型号的奔驰车模型,是先发动引擎,然后启动,然后停止,没有喇叭
    */
    public BenzModel getBBenzModel(){
    this.sequence.clear();
    this.sequence.add("engine boom");
    this.sequence.add("start");
    this.sequence.add("stop");
    this.benzBuilder.setSequence(this.sequence);
    return (BenzModel)this.benzBuilder.getCarModel();
    }
    ...
}

在这个导演类中就可以任意的指派方法的执行顺序了

于是,在Client方法中调用只需如下:

public class Client {
    public static void main(String[] args) {
        Director director = new Director();
        //1W辆A类型的奔驰车
        for(int i=0;i<10000;i++){
        director.getABenzModel().run();
        }
        //100W辆B类型的奔驰车
        for(int i=0;i<1000000;i++){
        director.getBBenzModel().run();
        }
        //1000W量C类型的宝马车
        for(int i=0;i<10000000;i++){
        director.getCBMWModel().run();
        }
    }
}

对该案例的总结:
(1)CarModel 以及两个实现类 BenzModel 和 BMWModel 叫做产品类(Product Class),这个产品类实现了模板方法模式,也就是有模板方法和基本方法。
(2)CarBuilder 以及两个实现类 BenzBuilder 和 BMWBuilder 叫做建造者(Builder Class)。
(3)Director 类叫做导演类(Director Class),负责安排已有模块的顺序,然后告诉 Builder 开始建造
这就已经实现了建造者模式了。

总结:

四个要素
产品类:一般是一个较为复杂的对象,也就是说创建对象的过程比较复杂,一般会有比较多的代码量。在本类图中,产品类是一个具体的类,而非抽象类。实际编程中,产品类可以是由一个抽象类与它的不同实现组成,也可以是由多个抽象类与他们的实现组成。
抽象建造者:引入抽象建造者的目的,是为了将建造的具体过程交与它的子类来实现。这样更容易扩展。一般至少会有两个抽象方法,一个用来建造产品,一个是用来返回产品。
建造者:实现抽象类的所有未实现的方法,具体来说一般是两项任务:组建产品;返回组建好的产品。
导演类:负责调用适当的建造者来组建产品,导演类一般不与产品类发生依赖关系,与导演类直接交互的是建造者类。一般来说,导演类被用来封装程序中易变的部分。

注意与工厂设计模式的区别
工厂设计模式侧重于生产的过程,即对象的创建,而建造者模式侧重的是通过不同的执行顺序,让产品类中的调用顺序不同产生了不同的效能。

使用建造者模式的好处:
1.使用建造者模式可以使客户端不必知道产品内部组成的细节。
2.具体的建造者类之间是相互独立的,对系统的扩展非常有利。
3.由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。

使用建造模式的场合:

1.创建一些复杂的对象时,这些对象的内部组成构件间的建造顺序是稳定的,但是对象的内部组成构件面临着复杂的变化。
2.要创建的复杂对象的算法,独立于该对象的组成部分,也独立于组成部分的装配方法时。

发布了46 篇原创文章 · 获赞 3 · 访问量 2万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章