【java设计模式】之 建造者(Builder)模式

简介:

目录(?)[+]

        我们还是举上一节的例子:生产汽车。上一节我们通过模板方法模式控制汽车跑起来的动作,那么需求是无止境的,现在如果老板又增加了额外的需求:汽车启动、停止、鸣笛引擎声都由客户自己控制,他想要什么顺序就什么顺序,那该如何做呢?

1. 汽车无休止的改造

        假如现在要生产两种车,奔驰和宝马,这两辆车都有共性,我们所需要关注的是单个车的运行过程,这才是老板所关心的点所在。我们先这样想,针对这个需求,我们要找到一个切入点,那就是产品类,每个车都是一个产品,那么在产品类中我们可以控制车的运行顺序,这样每个车都可以拥有自己想要的顺序了。基于此,我们设计如下类图:

         我们看到CarModel中有个setSequence方法,通过传入一个ArrayList来控制运行顺序,run方法根据这个ArrayList中保存的顺序执行,然后奔驰车和宝马车分别继承这个CarModel即可,这貌似是很好的实现,它很像上一节的模板方法模式,只是多了个方法可以设置运行顺序。我们看一下CarModel具体代码的实现:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public abstract class CarModel {  
  2.       
  3.     private ArrayList<String> sequence = new ArrayList<String>(); //维护一个ArrayList保存执行命令关键字  
  4.       
  5.     protected abstract void start();  
  6.     protected abstract void stop();  
  7.     protected abstract void alarm();  
  8.     protected abstract void engineBoom();  
  9.       
  10.     final public void run() {  
  11.         for(int i = 0; i < this.sequence.size(); i ++) { //根据ArrayList中保存的顺序执行相应的动作  
  12.             String actionName = this.sequence.get(i);  
  13.             if(actionName.equalsIgnoreCase("start")) {  
  14.                 this.start(); //启动汽车  
  15.             } else if(actionName.equalsIgnoreCase("stop")) {  
  16.                 this.stop(); //停止汽车  
  17.             } else if(actionName.equalsIgnoreCase("alarm")) {  
  18.                 this.alarm(); //汽车鸣笛  
  19.             } else if(actionName.equalsIgnoreCase("engine boom")) {  
  20.                 this.engineBoom(); //汽车轰鸣  
  21.             }  
  22.         }  
  23.     }  
  24.       
  25.     final public void setSequence(ArrayList<String> sequence) { //获得执行顺序的命令,即一个ArrayList  
  26.         this.sequence = sequence;  
  27.     }  
  28. }  
        CarModel中的setSequence方法允许客户自己设置一个顺序,我们看看子类的实现:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class BenzModel extends CarModel {  
  2.   
  3.     @Override  
  4.     protected void start() {  
  5.         System.out.println("奔驰启动……");  
  6.     }  
  7.   
  8.     @Override  
  9.     protected void stop() {  
  10.         System.out.println("奔驰停止……");  
  11.     }  
  12.   
  13.     @Override  
  14.     protected void alarm() {  
  15.         System.out.println("奔驰鸣笛……");  
  16.     }  
  17.   
  18.     @Override  
  19.     protected void engineBoom() {  
  20.         System.out.println("奔驰轰鸣");  
  21.     }  
  22.   
  23. }  
  24. //宝马就略了……一样的  
        下面我们增加一个测试类实现该需求:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class Client {  
  2.   
  3.     public static void main(String[] args) {  
  4.         BenzModel benz = new BenzModel();  
  5.         //存放run顺序  
  6.         ArrayList<String> sequence = new ArrayList<String>();  
  7.         sequence.add("engine boom"); //老板说:跑之前先轰鸣比较帅!  
  8.         sequence.add("start");  
  9.         sequence.add("stop");  
  10.         //我们把这个顺序赋予奔驰  
  11.         benz.setSequence(sequence);  
  12.         benz.run();  
  13.     }  
  14. }  
        这样好像已经顺利完成了任务了,但是别忘了,我们这只是满足了一个需求,如果下一个需求是宝马车只轰鸣,再下一个需求是奔驰车只跑不停……等等……那岂不是要一个个写测试类来实现?显然这不是我们想要的。

        我们可以这样做:为每种产品模型定义一个建造者,你要啥顺序直接告诉建造者,由建造者来建造即可,于是我们重新设计类图:


        我们增加了一个CarBuilder类,由它来组装各个车模型,要什么类型的顺序就由相关的子类去完成即可,我们来看看CarBuilder的代码:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public abstract class CarBuilder {  
  2.     //建造一个模型,你要给我一个顺序要求  
  3.     public abstract void setSequence(ArrayList<String> sequence);  
  4.     //设置完毕顺序后,就可以直接拿到这个车辆模型了  
  5.     public abstract CarModel getCarModel();  
  6. }  
        很简单,每个车辆模型都要有确定的运行顺序,然后才能返回一个车辆模型,奔驰车和宝马车组装者的代码如下:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class BenzBuilder extends CarBuilder {  
  2.   
  3.     private BenzModel benz = new BenzModel(); //奔驰车模型  
  4.       
  5.     @Override  
  6.     public void setSequence(ArrayList<String> sequence) {  
  7.         this.benz.setSequence(sequence); //设置奔驰车模型的运行顺序  
  8.     }  
  9.   
  10.     @Override  
  11.     public CarModel getCarModel() {  
  12.         return this.benz; //将这个模型返回  
  13.     }  
  14. }  
  15. //宝马车一样,不写了……  
        现在两辆车的组装者都写好了,现在我们写一个测试类来测试一下:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class Client {  
  2.   
  3.     public static void main(String[] args) {  
  4.   
  5.         //存放run顺序  
  6.         ArrayList<String> sequence = new ArrayList<String>();  
  7.         sequence.add("engine boom");  
  8.         sequence.add("start");  
  9.         sequence.add("stop");  
  10.         //要用这个顺序造一辆奔驰  
  11.         BenzBuilder benzBuilder = new BenzBuilder();  
  12.         //把顺序给奔驰组装者  
  13.         benzBuilder.setSequence(sequence);  
  14.         //奔驰组装者拿到顺序后就给你生产一辆来  
  15.         BenzModel benz = (BenzModel) benzBuilder.getCarModel();  
  16.         benz.run();  
  17.     }  
  18.   
  19. }  
       如果我要生产一辆宝马车,只需要换成宝马车的组装者即可,这样我们不用直接访问产品类了,全部访问组装者就行,是不是感觉到很方便,我管你怎么生产,我扔给你个顺序,你给我弄辆车出来,要的就是这种效果!

        可是人的需求是个无底洞,特别是老板,他哪天不爽了,又要换顺序,这样还是挺麻烦的,四个过程(start、stop、alarm、engine boom)按排列组合也有很多中情况,我们不能保证老板想要哪种顺序,咋整?无奈,我们只能使出最后的杀手锏了,找个设计师过来指挥各个时间的先后顺序,然后为每种顺序指定一个代码,你说一种我们立刻就给你生产!我们再修改一下类图……

        类图看着有点复杂,其实不然,只是在原来的基础上增加了一个Director类充当着设计师的角色,负责按照指定的顺序生产模型,比如我们要一个A顺序的奔驰车,B顺序的奔驰车,A顺序的宝马车,B顺序的宝马车……等等,我们来看下Director类的代码:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class Director {  
  2.     private ArrayList<String> sequence = new ArrayList<String>();  
  3.     private BenzBuilder benzBuilder = new BenzBuilder();  
  4.     private BWMBuilder bwmBuilder = new BWMBuilder();  
  5.       
  6.     //A顺序的奔驰车  
  7.     public BenzModel getABenzModel() {  
  8.         this.sequence.clear();  
  9.         this.sequence.add("start");  
  10.         this.sequence.add("stop");  
  11.         //返回A顺序的奔驰车  
  12.         this.benzBuilder.setSequence(sequence);  
  13.         return (BenzModel) this.benzBuilder.getCarModel();  
  14.     }  
  15.       
  16.     //B顺序的奔驰车  
  17.     public BenzModel getBBenzModel() {  
  18.         this.sequence.clear();  
  19.         this.sequence.add("engine boom");  
  20.         this.sequence.add("start");  
  21.         this.sequence.add("stop");  
  22.         //返回B顺序的奔驰车  
  23.         this.benzBuilder.setSequence(sequence);  
  24.         return (BenzModel) this.benzBuilder.getCarModel();  
  25.     }  
  26.       
  27.     //C顺序的宝马车  
  28.     public BenzModel getCBWMModel() {  
  29.         this.sequence.clear();  
  30.         this.sequence.add("start");  
  31.         this.sequence.add("alarm");  
  32.         this.sequence.add("stop");  
  33.         //返回C顺序的宝马车  
  34.         this.bwmBuilder.setSequence(sequence);  
  35.         return (BenzModel) this.bwmBuilder.getCarModel();  
  36.     }  
  37.   
  38.     //D顺序的宝马车  
  39.     public BenzModel getDBWMModel() {  
  40.         this.sequence.clear();  
  41.         this.sequence.add("engine boom");  
  42.         this.sequence.add("start");  
  43.         //返回D顺序的宝马车  
  44.         this.bwmBuilder.setSequence(sequence);  
  45.         return (BenzModel) this.bwmBuilder.getCarModel();  
  46.     }  
  47.       
  48.     //还有很多其他需求,设计师嘛,想啥需求就给你弄啥需求  
  49. }  
        有了这样一个设计师,我们的测试类就更容易处理了,比如现在老板要10000辆A类奔驰车,100000辆B类奔驰车,20000C类型宝马车,D类型不要:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class Client {  
  2.   
  3.     public static void main(String[] args) {  
  4.   
  5.         Director director = new Director();  
  6.           
  7.         for(int i = 0; i < 10000; i ++) {  
  8.             director.getABenzModel();  
  9.         }  
  10.           
  11.         for(int i = 0; i < 100000; i ++) {  
  12.             director.getBBenzModel();  
  13.         }  
  14.           
  15.         for(int i = 0; i < 20000; i ++) {  
  16.             director.getCBWMModel();  
  17.         }  
  18.     }  
  19.   
  20. }  
        是不是很清晰很简单,我们重构代码的最终第就是简单清晰。这就是建造者模式。

2. 建造者模式的定义

        我们来看看建造者模式的一般定义:Separate the construction of a complex object from its representation so that the same construction process can create different representations. 即:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。比如上面的例子,我们可以用同样的顺序构造不同的车。建造者模式的通用类图如下:

        Product是最终的产品类,Builder是建造者,Director是指挥者。Director负责安排已有模块的顺序,然后告诉Builder开始建造。

3. 建造者模式的优点

        1)封装性:使用建造者模式可以是客户端不必知道产品内部组成的细节。

        2)建造者独立,容易扩展:BenzBuilder和BMWBuilder是相互独立的,对系统扩展非常有利。

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

4. 建造者模式的使用场景

        1)相同的方法,不同的执行顺序,产生不同的事件结果时,可以使用建造者模式。

        2)多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不想同时,可以使用建造者模式。

        3)产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这时候可以使用建造者模式。

        4)在对象创建过程中会使用到系统的一些其他对象,这些对象在产品对象的创建过程中不易得到,也可以采用建造者模式封装该对象的创建过程。这种场景只能是一个补偿的方法,因为一个对象不容易获得,而在设计阶段竟然没有发现,而要通过设计这模式来柔化创建过程,本身设计已经出问题了。

        到这里,我们会发现,建造者模式和工厂方法模式有点像。但是两者有区别:建造者模式关注的是零件类型和装配工艺(顺序),而工厂模式是创建一个对象,这是最大不同的地方。

        创建者模式就介绍这么多吧,如有错误之处,欢迎留言指正~

        相关阅读:http://blog.csdn.net/column/details/des-pattern.html

_____________________________________________________________________________________________________________________________________________________

-----乐于分享,共同进步!

-----更多文章请看:http://blog.csdn.net/eson_15

相关文章
|
10天前
|
设计模式 SQL 算法
设计模式了解哪些,模版模式
设计模式了解哪些,模版模式
19 0
|
6天前
|
设计模式 Java 数据库
小谈设计模式(2)—简单工厂模式
小谈设计模式(2)—简单工厂模式
|
11天前
|
设计模式 监控 Java
设计模式 - 观察者模式(Observer):Java中的战术与策略
【4月更文挑战第7天】观察者模式是构建可维护、可扩展系统的关键,它在Java中通过`Observable`和`Observer`实现对象间一对多的依赖关系,常用于事件处理、数据绑定和同步。该模式支持事件驱动架构、数据同步和实时系统,但需注意避免循环依赖、控制通知粒度,并关注性能和内存泄漏问题。通过明确角色、使用抽象和管理观察者注册,可最大化其效果。
|
3天前
|
设计模式 算法 Java
小谈设计模式(30)—Java设计模式总结
小谈设计模式(30)—Java设计模式总结
|
4天前
|
设计模式 存储 Java
Java设计模式:解释一下单例模式(Singleton Pattern)。
`Singleton Pattern`是Java中的创建型设计模式,确保类只有一个实例并提供全局访问点。它通过私有化构造函数,用静态方法返回唯一的实例。类内静态变量存储此实例,对外仅通过静态方法访问。
12 1
|
6天前
|
设计模式 Java
小谈设计模式(9)—工厂方法模式
小谈设计模式(9)—工厂方法模式
|
8天前
|
设计模式 算法 Java
23种设计模式,模板方法模式的概念优缺点以及JAVA代码举例
【4月更文挑战第10天】模板方法模式是一种行为设计模式,它定义了一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些特定步骤。
11 0
|
9天前
|
设计模式 Java
23种设计模式,状态模式的概念优缺点以及JAVA代码举例
【4月更文挑战第9天】状态模式是一种行为设计模式,允许一个对象在其内部状态改变时改变它的行为,这个对象看起来似乎修改了它的类。
25 4
|
11天前
|
设计模式 Java
23种设计模式,命令模式的概念优缺点以及JAVA代码举例
【4月更文挑战第7天】命令模式是一种行为设计模式,它将请求或简单操作封装为一个对象。这种模式允许用户通过调用对象来参数化其他对象的方法,并能保存、排队和执行方法调用。
16 1
|
11天前
|
设计模式 缓存 安全
分析设计模式对Java应用性能的影响,并提供优化策略
【4月更文挑战第7天】本文分析了7种常见设计模式对Java应用性能的影响及优化策略:单例模式可采用双重检查锁定、枚举实现或对象池优化;工厂方法和抽象工厂模式可通过对象池和缓存减少对象创建开销;建造者模式应减少构建步骤,简化复杂对象;原型模式优化克隆方法或使用序列化提高复制效率;适配器模式尽量减少使用,或合并多个适配器;观察者模式限制观察者数量并使用异步通知。设计模式需根据应用场景谨慎选用,兼顾代码质量和性能。