博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【设计模式】建造者模式详解
阅读量:4078 次
发布时间:2019-05-25

本文共 4195 字,大约阅读时间需要 13 分钟。

建造者模式也叫生成器模式,具有封装性、易扩展等优势,其定义为:将一个复杂对象的构建与他的表示分离,使得同样的构建过程可以创建不同的表示。

    假定我们现在接到一个需求,要造出一辆奔驰,那么我们可以做如下设计:

    首先,本着抽象原则,我们肯定是会先定义一个接口Car,里面包含了车子都会有的部件,如下:

public abstract class Car {
/** 引擎 */ private String engine; /** 车身 */ private String bodywork; /** 底盘 */ private String chassis; /** 轮胎 */ private String tyre; @Override public String toString() { return new ToStringBuilder(this) .append("engine", engine) .append("bodywork", bodywork) .append("chassis", chassis) .append("tyre", tyre) .toString(); } //省略get set方法。。。}

    接下来,来定义我们的奔驰,此处奔驰与普通车的特殊在于他有一个全景天窗,我们来看Benz:

public class Benz extends Car {
public void sunroof () { System.out.println("我有全景天窗!"); }}

    要建造一辆奔驰,那么接下来我们需要一个建造者来完成这件事情,如下先定义出通用建造者的接口:

public interface ICarBuilder {
/** * 建造引擎 */ void buildEngine(); /** * 建造车身 */ void buildBodywork(); /** * 建造底盘 */ void buildChassis(); /** * 建造轮胎 */ void buildTyre(); /** * 建造车子 */ Car build();}

    接下来我们来实现奔驰建造者,用以制造我们的奔驰:

public class BenzBuilder implements ICarBuilder {
private static Car benz; public BenzBuilder() { } public void buildEngine() { benz.setEngine("奔驰引擎"); } public void buildBodywork() { benz.setBodywork("奔驰车身"); } public void buildChassis() { benz.setChassis("奔驰底盘"); } public void buildTyre() { benz.setTyre("奔驰轮胎"); } public Car build () { benz = new Benz(); this.buildEngine(); this.buildBodywork(); this.buildChassis(); this.buildTyre(); return benz; }}

    通过奔驰的建造者,我们只要调用build方法,就可以实现奔驰的建造,这是一种最简易化的建造者模式。我们来看一下建造奔驰:

public static void main(String[] args) {    ICarBuilder benzBuilder = new BenzBuilder();    Car benz = benzBuilder.build();    System.out.println(benz);}

输出结果:

org.white.common.test.design.model.Benz@3af49f1c[engine=奔驰引擎,bodywork=奔驰车身,chassis=奔驰底盘,tyre=奔驰轮胎]

    可以看到奔驰已经建造成功,那么接下来,加入又需求我们建造一辆沃尔沃,我们怎么办呢?接下来就要用到完整版的建造者模式了,类图如下:

建造者模式

    这里引入了Director类来作为一个控制器,根据不同的指令来建造不同的车,这里需要对上面BenzBuilder类作如下修改:

/** 构造方法中新建对象 */    public BenzBuilder() {        benz = new Benz();    }    /** build方法不再自行构建,责任交还给Director类,之前是自己充当了Director类的职责 */    public Car build () {        return benz;    }

    接下来来看CarDirector类的代码如下:

public class CarDirector {
/** * 使用不同的建造器建造不同的车 */ public Car construct(ICarBuilder builder) { builder.buildEngine(); builder.buildBodywork(); builder.buildChassis(); builder.buildTyre(); return builder.build(); }}

    可以通过代码看出,我们传入奔驰的建造器就可以建造一台奔驰,那么按现在的代码结构这里我们要建造一台沃尔沃,只需要新加一个Volvo类和一个VolvoBuilder类就可以了,不会对原有代码造成影响,后续不论造什么车,都可以直接加上各自的建造器和各自的车实体类就可以完成,不需要修改原有代码实现,这样就会获得一个良好的扩展性。接下来我们来看建造一台沃尔沃所需代码:

    首先是Volo类:

public class Volvo extends Car {
public void safe () { System.out.println("我有自动驾驶系统,我很安全!"); }}

    接下来是VolvoBuilder类:

public class VolvoBuilder implements ICarBuilder {
private static Car volvo; public VolvoBuilder() { volvo = new Volvo(); } public void buildEngine() { volvo.setEngine("沃尔沃引擎"); } public void buildBodywork() { volvo.setBodywork("沃尔沃车身"); } public void buildChassis() { volvo.setChassis("沃尔沃底盘"); } public void buildTyre() { volvo.setTyre("沃尔沃轮胎"); } public Car build() { return volvo; }}

    接下来写测试代码测试是否建造成功:

public static void main(String[] args) {    CarDirector director = new CarDirector();    Car benz = director.construct(new BenzBuilder());    Car volvo = director.construct(new VolvoBuilder());    System.out.println(benz.toString());    System.out.println(volvo.toString());}

    运行结果如下

org.white.common.test.design.model.Benz@3af49f1c[engine=奔驰引擎,bodywork=奔驰车身,chassis=奔驰底盘,tyre=奔驰轮胎]org.white.common.test.design.model.Volvo@19469ea2[engine=沃尔沃引擎,bodywork=沃尔沃车身,chassis=沃尔沃底盘,tyre=沃尔沃轮胎]

    可以看到奔驰和沃尔沃都建造成功了。

下面来总结建造者模式的优点:

  1. 强封装性:作为client,我们并不需要知道建造一辆车的过程,一切建造过程都封装在各自的建造器(Builder)中,我们只需发出建造奔驰的指令,则可以通知BenzBuilder建造一台奔驰,并不知道他到底是怎么制造的。
  2. 强扩展性:通过上面的代码可以看出,我们需要建造其他车,是很容易扩展的,而且不会对原有代码造成任何影响,这一点是很重要的。
  3. 强差异化:各自的建造过程由各自负责,所以可以实现各自的细节化

欢迎关注个人博客:blog.scarlettbai.com

你可能感兴趣的文章
fastcgi_param 详解
查看>>
搞定Java面试中的数据结构问题
查看>>
React Native(一):搭建开发环境、出Hello World
查看>>
二叉树的非递归遍历
查看>>
【leetcode】Linked List Cycle (python)
查看>>
【leetcode】Sum Root to leaf Numbers
查看>>
如何成为编程高手
查看>>
本科生的编程水平到底有多高
查看>>
从mysql中 导出/导入表及数据
查看>>
几个常用的Javascript字符串处理函数 spilt(),join(),substring()和indexof()
查看>>
9、VUE面经
查看>>
Golang 数据可视化利器 go-echarts ,实际使用
查看>>
mysql 跨机器查询,使用dblink
查看>>
Jenkins + Docker + SpringCloud 微服务持续集成 - 单机部署(二)
查看>>
C#控件集DotNetBar安装及破解
查看>>
Winform多线程
查看>>
C# 托管与非托管
查看>>
Node.js中的事件驱动编程详解
查看>>
nodejs内存控制
查看>>
MongoDB 数据文件备份与恢复
查看>>