在一个周三,快要下班了,老大突然又拉住我,喜滋滋的告诉我“牛叉公司很满意我们做的模型,又签订了一个合同,把奔驰、宝马的车辆模型都交给我们公司制作了,不过这次又额外增加了一个新需求:汽车的启动、停止、喇叭声音、引擎声音都有客户自己控制,他想什么顺序就什么顺序,这个没问题吧?”。

  看着老大殷切的目光,我还能说啥,肯定的点头,“没问题! ”,加班加点做呗,“再苦再累就当自己二百五 再难再险就当自己二皮脸 与君共勉! ”这句话说出了我的心声。那任务是接下来,我们怎么做实现呢?

  首先我们想了,奔驰、宝马都是一个产品,他们有共有的属性,牛叉公司关心的是单个模型,奔驰模型 A 是先有引擎声音,然后再启动;奔驰模型 B 呢是先启动起来,然后再有引擎声音,这才是牛叉公司要关关心的,那到我们老大这边呢,就是满足人家的要求,要什么顺序就立马能产生什么顺序的模型出来,我呢就负责把老大的要求实现掉,而且还要是批量的,看不懂?没关系,继续看下去,首先由我生产出 N多个奔驰和宝马车辆模型,这些车辆模型的都有 run 方法,但是具体到每一个模型的 run 方法可能中间的执行任务的顺序是不同的,老大说要啥顺序,我就给啥顺序,最终客户买走后只能是既定的模型,还是没听明白,我们继续,我们先把我们最基本的对象 Product 在类图中表明出来:

  我们定义了一个 CarModel 的抽象类,其中 run 和 setSequence 是由抽象类实现的,其他都是子类自己实现,那这个是否可以解决这个问题呢?应该可以,我们把代码实现出来,先看 CarModel.java 程序:

 package com.pattern.builder;

 import java.util.ArrayList;

 /**
* 定义一个车辆模型的抽象类,所有的车辆模型都继承这里
* @author http://www.cnblogs.com/initial-road/
*
*/
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("start".equalsIgnoreCase(actionName)){
this.start(); //开启汽车
}else if("stop".equalsIgnoreCase(actionName)){
this.stop(); //停止汽车
}else if("alarm".equalsIgnoreCase(actionName)){
this.alarm(); //喇叭开始叫了
}else if("engine boom".equalsIgnoreCase(actionName)){
this.engineBoom(); //引擎开始轰鸣
}
}
} /**
* 可以修改顺序
* @param sequence
*/
final public void setSequence(ArrayList<String> sequence){
this.sequence = sequence;
} }

  其中 setSequence 方法是允许客户自己设置一个顺序,是要先跑起来在有引擎声音还是先有引擎声音再跑起来,还是说那个喇叭就不要响,对于一个具体的模型永远都固定的,那这个事由牛叉告诉我们,有 1W 件事这样的, 1W 件事那样的顺序,目前的设计都能满足这个要求。run 方法使用了一个数组的遍历,确定那个是先执行,程序比较简单,不多说了,我们继续看两个实现类,先看 BenzModel.java 程序:

 package com.pattern.builder;

 /**
* 奔驰车模型
* @author http://www.cnblogs.com/initial-road/
*
*/
public class BenzModel extends CarModel { @Override
protected void alarm() {
System.out.println("奔驰车的喇叭声音是这个样子的...");
} @Override
protected void engineBoom() {
System.out.println("奔驰车的引擎是这个声音的...");
} @Override
protected void start() {
System.out.println("奔驰车跑起来是这个样子的...");
} @Override
protected void stop() {
System.out.println("奔驰车应该这样停车...");
} } 再看 BMWModel.java 程序:
package com.pattern.builder; /**
* 宝马车模型
* @author http://www.cnblogs.com/initial-road/
*
*/
public class BenzModel extends CarModel { @Override
protected void alarm() {
System.out.println("宝马车的喇叭声音是这个样子的...");
} @Override
protected void engineBoom() {
System.out.println("宝马车的引擎是这个声音的...");
} @Override
protected void start() {
System.out.println("宝马车跑起来是这个样子的...");
} @Override
protected void stop() {
System.out.println("宝马车应该这样停车...");
} }

  两个实现类都完成,我们再来看牛叉公司要的要求,我先要 1 个奔驰的模型,这个模型的要求是跑的时候,先发动引擎,然后再挂档启动,然后停下来,不需要喇叭,那怎么实现呢:

 package com.pattern.builder;

 import java.util.ArrayList;

 /**
* 最终客户开始使用这个模型
* @author http://www.cnblogs.com/initial-road/
*
*/
public class Client { public static void main(String[] args) { /**
* 客户告诉牛叉公司,我要这样一个模型,然后牛叉公司就告诉我老大
* 说要这样一个模型,这样一个顺序,然后我就来制造
*/
BenzModel benz = new BenzModel();
ArrayList<String> sequence = new ArrayList<String>(); sequence.add("engine boom"); //客户要求,run的时候先发动引擎
sequence.add("start"); //启动起来
sequence.add("stop"); //开了一段就停下来 //然后我们把这个顺序给奔驰车
benz.setSequence(sequence);
benz.run();
} }

  看,满足了牛叉公司的需求了,满足完了,还要下一个需求呀,然后是第 2 件宝马模型,只要启动,停止,其他的什么都不要,第 3 件模型,先喇叭,然后启动,然后停止,第 4件... ,直到把你逼疯为止,那怎么办?我们修改程序,满足这种变态需求,好,看我如何修改,先修改类图:

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

 package com.pattern.builder;

 import java.util.ArrayList;

 /**
* 要什么顺序的车,你说,我给建造出来
* @author http://www.cnblogs.com/initial-road/
*
*/
public abstract class CarBuilder { /**
* 建造一个模型,你要给我一个顺序要,就是组装顺序
*/
public abstract void setSequence(ArrayList<String> sequence); /**
* 设置完毕顺序后,就可以直接拿到这个车辆模型
*/
public abstract CarModel getCarModel(); }

  这个抽象类比较简单,程序上也以后注释说明,不多说,我们看两个实现类,先看 BenzBuilder.java的程序代码:

 package com.pattern.builder;

 import java.util.ArrayList;

 /**
* 各种设施都给了,我们按照一定的顺序制造一个奔驰车
* @author http://www.cnblogs.com/initial-road/
*
*/
public class BenzBuilder extends CarBuilder {
private BenzModel benz = new BenzModel(); @Override
public CarModel getCarModel() {
return this.benz;
} @Override
public void setSequence(ArrayList<String> sequence) {
this.benz.setSequence(sequence);
} } 下面是 BMWBuilder.java 程序代码:
package com.pattern.builder; import java.util.ArrayList; /**
* 给定一个顺序,返回一个宝马车
* @author http://www.cnblogs.com/initial-road/
*
*/
public class BMWBuilder extends CarBuilder {
private BMWModel bmw = new BMWModel(); @Override
public CarModel getCarModel() {
return this.bmw;
} @Override
public void setSequence(ArrayList<String> sequence) {
this.bmw.setSequence(sequence);
} }

  程序很简单,很实用,这就是我最希望的,简单而又实用,我欣赏这样的程序员。那我们再来看我们Client.java 程序的修改:

 package com.pattern.builder;

 import java.util.ArrayList;

 /**
* 最终客户开始使用这个模型
* @author http://www.cnblogs.com/initial-road/
*
*/
public class Client { public static void main(String[] args) { /**
* 客户告诉牛叉公司,我要这样一个模型,然后牛叉公司就告诉我老大
* 说要这样一个模型,这样一个顺序,然后我就来制造
*/
ArrayList<String> sequence = new ArrayList<String>();
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();
} } 那如果我再想要个同样顺序的宝马车呢?很简单, Client.java 程序如下:
package com.pattern.builder; import java.util.ArrayList; /**
* 最终客户开始使用这个模型
* @author http://www.cnblogs.com/initial-road/
*
*/
public class Client { public static void main(String[] args) {
//存放run的顺序
ArrayList<String> sequence = new ArrayList<String>();
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(); //按照同样的顺序,我再要一个宝马
BMWBuilder bmwBuilder = new BMWBuilder();
bmwBuilder.setSequence(sequence);
BMWModel bmw =(BMWModel) bmwBuilder.getCarModel();
bmw.run(); } }

  看,是不是比刚开始直接访问产品类(Procuct) 简单了很多吧,那这有个这样的需求,这四个过程(start,stop,alarm,engineBoom)按照排列组合有很多种,那我们怎么满足种需求呢?也就是要有个类来安排这几个方法组合,就像导演安排演员一样,那个先出场那个后出场,那个不出场,我们这个也叫导演类,那我们修改一下类图:

  增加了 Director 类,这个类是做了层封装,类中的方法说明如下:
    A 型号的奔驰车辆模型是只有启动(start)、停止(stop) 方法,其他的引擎声音、喇叭都没有;
    B 型号的奔驰车是先发动引擎(engine boom) , 然后启动(star), 再然后停车(stop), 没有喇叭;
    C 型号的宝马车是先喇叭叫一下(alarm),然后(start) , 再然后是停车(stop) ,引擎不轰鸣;
    D 型号的宝马车就一个启动(start) ,然后一路跑到黑,永动机,没有停止方法,没有喇叭,没有引擎轰鸣;
    E 型号、 F 型号…等等,可以有很多, 启动(start) 、停止(stop) 、喇叭(alarm) 、引擎轰鸣(engine boom);
  这四个方法在这个类中可以随意的自由组合,有几种呢?好像是排列组合,这个不会算,高中数学没学好,反正有很多种了,这里都可以实现。
  我们看一下代码实现, Director.java 代码如下:

 package com.pattern.builder;

 import java.util.ArrayList;

 /**
* 导演安排顺序,生产车辆模型
* @author http://www.cnblogs.com/initial-road/
*
*/
public class Director {
private ArrayList<String> sequence = new ArrayList<String>();
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(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();
} /**
* C型号的宝马车是先按下喇叭(炫耀嘛),然后启动,然后停止
*/
public BMWModel getCBMWModel(){
this.sequence.clear(); this.sequence.add("alarm");
this.sequence.add("start");
this.sequence.add("stop"); this.bmwBuilder.setSequence(sequence);
return (BMWModel) this.bmwBuilder.getCarModel();
} /**
* D类型的宝马车车只有一个功能,就是跑,启动起来就跑,永远不停止,牛叉
*/
public BMWModel getDBMWModel(){
this.sequence.clear(); this.sequence.add("start"); this.bmwBuilder.setSequence(this.sequence);
return (BMWModel) this.benzBuilder.getCarModel();
}
}

  大家看一下程序中有很多 this 调用,这个我一般是这样要求项目组成员的,如果你要调用类中的成员变量或方法,需要在前面加上 this 关键字,不加也能正常的跑起来,但是不清晰,加上 this 关键字,我就是要调用本类中成员变量或方法,而不是本方法的中的一个变量,还有 super 方法也是一样,是调用父类的的成员变量或者方法,那就加上这个关键字,不要省略,这要靠约束,还有就是程序员的自觉性,他要是死不悔改,那咱也没招。

  上面每个方法都一个 this.sequence.clear() ,这个估计你一看就明白,但是做为一个系统分析师或是技术经理一定要告诉告诉项目成员, ArrayList 和 HashMap 如果定义成类的成员变量,那你在方法中调用一定要做一个 clear 的动作,防止数据混乱,这个如果你发生过一次问题的话,比如 ArrayList 中出现一个“出乎意料”的数据,而你又花费了几个通宵才解决这个问题,那你会有很深刻的印象。

然后 Client 程序就只与 Director 打交道了,牛叉公司要 A 类型的奔驰车 1W 辆, B 类型的奔驰车 100W辆, C 类型的宝马车 1000W 辆, D 类型的我不要:

 package com.pattern.builder;

 import java.util.ArrayList;

 /**
* 这里是牛叉公司的天下,他要啥我们给啥
* @author http://www.cnblogs.com/initial-road/
*
*/
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<100000000;i++){
director.getCBMWModel().run();
}
} }

  清晰,简单吧,我们写程序重构的最终目的就是这个,简单,清晰,代码是让人看的,不是写完就完事了,我一直在教育我带的团队, Java 程序不是像我们前辈写那个二进制代码、汇编一样,写完基本上就自己能看懂,别人看就跟看天书一样,现在的高级语言,要像写中文汉字一样,你写的,别人能看懂。

  整个程序编写完毕,而且简洁明了,这就是建造者模式,中间有几个角色需要说明一下:

  Client 就是牛叉公司,这个到具体的应用中就是其他的模块或者页面;

  CarModel 以及两个实现类 BenzModel 和 BMWModel 叫做产品类(Product Class) ,这个产品类实现了模板方法模式,也就是有模板方法和基本方法,这个参考上一节的模板方法模式;
  CarBuilder 以及两个实现类 BenzBuilder 和 BMWBuilder 叫做建造者(Builder Class) ,在上面的那个例子中就是我和我的团队,负责建造 Benz 和 BMW 车模,按照指定的顺序;

  Director 类叫做导演类(Director Class), 负责安排已有模块的顺序,然后告诉 Builder 开始建造,在上面的例子中就是我们的老大, Client 找到老大,说我要这个,这个,那个类型的车辆模型,然后老大就把命令传递给我,我和我的团队就开始拼命的建造,于是一个项目建设完毕了。

  大家看到这里估计就开始犯嘀咕了,这个建造者模式和工厂模式非常相似呀, Yes,是的,是非常相似,但是记住一点你就可以游刃有余的使用了:建造者模式最主要功能是基本方法的调用顺序安排,也就是这些基本方法已经实现了;而工厂方法则重点是创建,你要什么对象我创造一个对象出来,组装顺序则不是他关心的。

  建造者模式使用的场景,一是产品类非常的复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式是非常合适,我曾在一个银行交易类项目中遇到了这个问题,一个产品的定价计算模型有 N 多种,每个模型有固定的计算步骤,计算非常复杂,项目中就使用了建造者模式;二是“ 在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到”,这个是我没有遇到过的,创建过程中不易得到?那为什么在设计阶段不修正这个问题,创建的时候都不易得到耶!

24种设计模式--建造者模式【Builder Pattern】的更多相关文章

  1. 23种设计模式--建造者模式-Builder Pattern

    一.建造模式的介绍       建造者模式就是将零件组装成一个整体,用官方一点的话来讲就是将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示.生活中比如说组装电脑,汽车等等这些都是建 ...

  2. 设计模式 - 建造者模式 Builder Pattern

    简介 场景 在创建复杂对象时,用户无须关心该对象所包含的属性以及它们的组装方式,只需要指定复杂对象的类型和内容就可以构建它们. 模式定义 建造者模式:将一个复杂对象的构建与表示分离,使得同样的构建过程 ...

  3. 24种设计模式-策略模式(Strategy Pattern)

    一.优点: 1. 策略模式提供了管理相关的算法族的办法.策略类的等级结构定义了一个算法或行为族.恰当使用继承可以把公共的代码转移到父类里面,从而避免重复的代码. 2. 策略模式提供了可以替换继承关系的 ...

  4. 二十四种设计模式:建造者模式(Builder Pattern)

    建造者模式(Builder Pattern) 介绍将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 示例用同样的构建过程创建Sql和Xml的Insert()方法和Get()方 ...

  5. 【原】iOS设计模式之:建造者模式Builder Pattern,用于改进初始化参数

    本文主要讨论一下iOS中的Builder Pattern.与网上很多版本不同,本文不去长篇大论地解释建造者模式的概念,那些东西太虚了.设计模式这种东西是为了解决实际问题的,不能为了设计模式而设计模式, ...

  6. 【设计模式】建造者模式 Builder Pattern

    前面学习了简单工厂模式,工厂方法模式以及抽象工厂模式,这些都是创建类的对象所使用的一些常用的方法和套路, 那么如果我们创建一个很复杂的对象可上面的三种方法都不太适合,那么“专业的事交给专业人去做”,2 ...

  7. iOS设计模式之:建造者模式Builder Pattern,用于改进初始化参数

    转自:http://www.cnblogs.com/wengzilin/p/4365855.html 本文主要讨论一下iOS中的Builder Pattern.与网上很多版本不同,本文不去长篇大论地解 ...

  8. 设计模式系列之建造者模式(Builder Pattern)——复杂对象的组装与创建

    说明:设计模式系列文章是读刘伟所著<设计模式的艺术之道(软件开发人员内功修炼之道)>一书的阅读笔记.个人感觉这本书讲的不错,有兴趣推荐读一读.详细内容也可以看看此书作者的博客https:/ ...

  9. 乐在其中设计模式(C#) - 建造者模式(Builder Pattern)

    原文:乐在其中设计模式(C#) - 建造者模式(Builder Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 建造者模式(Builder Pattern) 作者:webabc ...

随机推荐

  1. Angular.js vs Ember.js

    Angular.js 拥抱 HTML/CSS Misko Hevery(Angular.js的开发者之一)回答了这一问题,他的主要观点如下: 在HTML中加入太多逻辑不是好做法.Angular.js只 ...

  2. [Android Framework]linux 文件系统

    目录名   bin 用户二进制工具 boot Linux内核镜像文件, 由bootloader程序读取并装载 dev 各种系统硬件设备 etc 系统配置文件及其他配置文件 home 用户工作目录 li ...

  3. 【Caffe 测试】Training LeNet on MNIST with Caffe

    Training LeNet on MNIST with Caffe We will assume that you have Caffe successfully compiled. If not, ...

  4. js模板引擎实现原理

    将html模板放入script标签中 // "> // ]]> 使用javascript开始解析模板 // )[^\t]*)'/g,"$1\r"). repl ...

  5. NIO组件Selector工作机制详解(下)

    转自:http://blog.csdn.net/haoel/article/details/2224069 五.  迷惑不解 : 为什么要自己消耗资源? 令人不解的是为什么我们的Java的New I/ ...

  6. MVC入门教程-视图中的Layout使用

    本文目标 1.能够重用Razor模板进行页面的组件化搭建 本文目录 1.母板页_Layout.cshtml 2.用户自定义控件 3.默认Layout引用的使用(_ViewStart.cshtml) 1 ...

  7. C# 可访问一致性不一致

    出现原因,其中一个是返回参数的访问级别小于函数的访问级别, 也就是说当定义一个返回参数的方法的时候,如果返回参数的访问级别低于方法的访问级别就会出现这样的错误,这个是可以理解的,如果返回的参数不能被访 ...

  8. JSON和JAVA的POJO的相互转换【转载】

    该类中用用到的jar包:参加文章<使用json-lib进行Java和JSON之间的转换[转载]> import java.util.Collection; import java.util ...

  9. chrome下老是弹出网页显示 true

    事实上这个问题是chrome下安装了一些插件的原因,一般来说是安装迅雷插件会出现这个问题,删除迅雷插件就好了.

  10. 以色列学生---debugger 构建

    http://eli.thegreenplace.net/2011/01/23/how-debuggers-work-part-1 http://eli.thegreenplace.net/