前言:设计模式的出现就是为了提高代码的重用率,实现高可用和可扩展性。不同的设计模式满足不同的需求,切记,需求千变万化,不要因为需求变态就而过分抱怨,记住,程序员应该向需求低头。
案例展示——Builder怎么用?
现在考虑这样的场景:现有一家汽车生产公司,主要生产宝马汽车和奔驰汽车两种车型,一直以来,该汽车公司都使用工厂方法模式生产同一个产品系列的车型或者使用抽象工厂模式生产不同产品族的车型,结果自然是顺风顺水,吃香的喝辣的。可是还是前言中所说的,用户的需求是千变万化的,永不满足的,这不用户提出了需求:汽车的启动,停止,喇叭声音,引擎声音都由用户自己控制,想让它们是什么顺序就什么顺序。这样的需求不可谓不过分,你说什么控制权都给你了,那我们自己的灵魂在哪?还要我们干什么?可用户是上帝,人总是要向钱低头的(圈起来,要考),毕竟或者才是硬道理!
有了需求,就需要我们分析讨论,制定计划了。我们发现,原来的工厂方法模式放在这里已经不合适了,因为工厂方法模式主要的工作是创建,其实说白了是我就只负责造零件,关于顺序等技术细节并不关注。这样的设计肯定是不行的,根据用户的需求,我们要控制的就是技术细节,也就是零件装配的顺序(通过装配细节顺序的控制以实现用户想要进行顺序控制的需求),想明白了这一点,我们要使用的设计模式也就明朗化了——使用建造者模式(Builder)。如下是设计类图:
分析: 宝马,奔驰都属于汽车产品,它们都有共有的属性。定义一个抽象类Car,用于封装汽车共有的属性,同时定义了一个模板方法run(),该方法是可以定制汽车启动,停止,喇叭,引擎顺序的。BaomaCar和BanchiCar都继承于抽象类Car,用于实现自己的业务逻辑。CarBuilder是一个汽车组装的抽象类,用来组装每辆汽车,要什么顺序的汽车由其相关子类实现。BaomaBuilder和BenchiBuilder是分别用于组装宝马车和奔驰车的实现类。Director是顶层的导演类,由于用户需要定制的无非就是:启动,停止,喇叭,引擎这四种过程,排列组合后总是有一个集合的,我们可以把这些集合疯转到一个类中,这样用户想要什么顺序的就给他生产什么顺序的,岂不是很方便:
-
getABaomaCar():组建A类型的宝马车:先start,后stop,没有alarm和engine boom
-
getBBaomaCar():组建B类型的宝马车:先engine boom,后start,最后stop,没有alarm
-
getCBenchiCar():组建C类型的奔驰车:先alarm,后start,最后stop,没有engine boom
-
getDBenchiCar():组建D类型的奔驰车:start后一直跑
下面是具体的代码实现:
//汽车抽象类
public abstract class Car {
//定义各个基本方法的执行顺序,用一个List封装
private ArrayList<String> sequence = new ArrayList<String>();
//把传递来的值赋给sequence
public final void setSequence(ArrayList<String> sequence) {
this.sequence = sequence;
}
//发动汽车
protected abstract void start();
//停止汽车
protected abstract void stop();
//鸣笛
protected abstract void alarm();
//启动引擎
protected abstract void engineBoom();
//模板方法:汽车奔跑
public final void run() {
//循环,哪个在前就执行哪个
for (int i = 0; i < this.sequence.size(); i++) {
String actionName = this.sequence.get(i);
if (actionName.equalsIgnoreCase("start")) {
this.start(); //启动汽车
} else if (actionName.equalsIgnoreCase("stop")) {
this.stop(); //停止汽车
} else if (actionName.equalsIgnoreCase("alarm")) {
this.alarm(); //鸣笛
} else if (actionName.equalsIgnoreCase("engine boom")) {
this.engineBoom(); //引擎轰鸣
}
}
}
}
//宝马车实现类
public class BaomaCar extends Car{
protected void start() {
System.out.println("宝马车应该这样启动。。。");
}
protected void stop() {
System.out.println("宝马车应该这样停止。。。");
}
protected void alarm() {
System.out.println("宝马车应该这样鸣笛。。。");
}
protected void engineBoom() {
System.out.println("宝马车引擎声是这样的。。。");
}
}
//奔驰车实现类
public class BenchiCar extends Car{
protected void start() {
System.out.println("奔驰车应该这样启动。。。");
}
protected void stop() {
System.out.println("奔驰车应该这样停止。。。");
}
protected void alarm() {
System.out.println("奔驰车应该这样鸣笛。。。");
}
protected void engineBoom() {
System.out.println("奔驰车引擎声是这样的。。。");
}
}
//汽车建造的抽象类
public abstract class CarBuilder {
//建造一辆车,给一个顺序要求
public abstract void setSequence(ArrayList<String> sequence);
//设置完毕顺序,给你生产一辆车
public abstract Car getCar();
}
//宝马车组装的实现类
public class BaomaBuilder extends CarBuilder{
//持有一个宝马车对象
private BaomaCar baoma = new BaomaCar();
//设置组装顺序
public void setSequence(ArrayList<String> sequence) {
this.baoma.setSequence(sequence);
}
//得到一辆宝马车
public Car getCar() {
return this.baoma;
}
}
//奔驰车组装的实现类
public class BenchiBuilder extends CarBuilder{
//持有一个奔驰车对象
private BenchiCar benchi = new BenchiCar();
//设置组装顺序
public void setSequence(ArrayList<String> sequence) {
this.benchi.setSequence(sequence);
}
//得到一辆奔驰车
public Car getCar() {
return this.benchi;
}
}
导演类(Director)代码实现如下:
public class Director {
//封装运行顺序
private ArrayList<String> sequence = new ArrayList<String>();
//持有一个奔驰车组装对象
private BenchiBuilder benchiBuilder = new BenchiBuilder();
//持有一个宝马车组装对象
private BaomaBuilder baomaBuilder = new BaomaBuilder();
/**
* 组建A类型的宝马车:先start,后stop,没有alarm和engine boom
*/
public BaomaCar getABaomaCar() {
//清理原先List中的缓存数据:注意
this.sequence.clear();
//定义执行顺序
this.sequence.add("start");
this.sequence.add("stop");
//按照定义顺序返回一辆宝马车
this.baomaBuilder.setSequence(this.sequence);
return (BaomaCar)this.baomaBuilder.getCar();
}
/**
* 组建B类型的宝马车:先engine boom,后start,最后stop,没有alarm
*/
public BaomaCar getBBaomaCar() {
//清理原先List中的缓存数据:注意
this.sequence.clear();
//定义执行顺序
this.sequence.add("engine boom");
this.sequence.add("start");
this.sequence.add("stop");
//按照定义顺序返回一辆宝马车
this.baomaBuilder.setSequence(this.sequence);
return (BaomaCar)this.baomaBuilder.getCar();
}
/**
* 组建C类型的奔驰车:先alarm,后start,最后stop,没有engine boom
*/
public BenchiCar getCBenchiCar() {
//清理原先List中的缓存数据:注意
this.sequence.clear();
//定义执行顺序
this.sequence.add("alarm");
this.sequence.add("start");
this.sequence.add("stop");
//按照定义顺序返回一辆宝马车
this.benchiBuilder.setSequence(this.sequence);
return (BenchiCar)this.benchiBuilder.getCar();
}
/**
* 组建D类型的奔驰车:start后一直跑
*/
public BenchiCar getDBenchiCar() {
//清理原先List中的缓存数据:注意
this.sequence.clear();
//定义执行顺序
this.sequence.add("start");
//按照定义顺序返回一辆宝马车
this.benchiBuilder.setSequence(this.sequence);
return (BenchiCar)this.benchiBuilder.getCar();
}
}
在一个场景中运行实现:
假如想要批量生产,直接加一个循环就行,很方便!
public class Client {
public static void main(String[] args) {
Director director = new Director();
System.out.println("=======生产A类性的宝马车=======");
director.getABaomaCar().run();
System.out.println("=======生产B类性的宝马车=======");
director.getBBaomaCar().run();
System.out.println("=======生产C类性的奔驰车=======");
director.getCBenchiCar().run();
System.out.println("=======生产D类性的奔驰车=======");
director.getDBenchiCar().run();
}
}
//结果如下:
=======生产A类性的宝马车=======
宝马车应该这样启动。。。
宝马车应该这样停止。。。
=======生产B类性的宝马车=======
宝马车引擎声是这样的。。。
宝马车应该这样启动。。。
宝马车应该这样停止。。。
=======生产C类性的奔驰车=======
奔驰车应该这样鸣笛。。。
奔驰车应该这样启动。。。
奔驰车应该这样停止。。。
=======生产D类性的奔驰车=======
奔驰车应该这样启动。。。
深入分析——Builder是什么?
Builder的定义
建造者模式也叫做生成器模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式的通用类图如下:
在建造者模式有如下4个角色:
-
Product产品类: 该类通常实现了模板方法模式,有模板方法和基本方法。
-
Builder抽象建造者: 该类用于规范产品的组建,一般由子类实现。
-
ConcreteBuilder具体建造者类: 实现抽象类中定义的所有方法,并且返回一个组建好的对象。
-
Director导演类: 负责安排已有模板的顺序,然后告诉Builder开始建造。
建造者模式的通用源代码如下:
//产品类
public class product {
public void doSomething() {
//业务逻辑
}
//模板方法
public void templateMethod() {
//逻辑顺序
}
}
//抽象建造者
publlic abstract class Builder {
//设置产品的不同部分
public abstract void setPart();
//建造产品
public abstract Product buildProduct();
}
//具体建造者
public class ConcreteBuilder extends Builder {
private Product product = new Product();
//设置产品零件
public void setPart() {
//逻辑处理
}
//组建一个产品
public Product buildProduct() {
return this.product
}
}
//导演类
public class Director {
private Builder builder = new ConcreateProduct();
//构建不同的产品
public Product getAProduct() {
builder.setPart();
//逻辑处理
return builder.buildProduct();
}
}
Builder的应用
1. 建造者模式的优点
-
封装性: 使用建造者模式可以使客户端不必知道产品内部的组成细节
-
建造者独立,容易扩展
-
便于控制细节风险: 由于具体建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何的影响
2. 使用场景
-
相同的方法,不同的执行顺序,产生不同的事件结果时可以使用该模式
-
多个部件或零件,都可以装配到一个对象中,但是产生的运行结果不相同时可以使用该模式
-
产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能时使用该模式
3. 建造者模式和工厂方法模式的区分
建造者模式最主要的功能是基本方法调用顺序的安排,也就是这些基本方法已经实现了,通俗的说就是零件的装配,顺序不同产生的对象也不同;而工厂方法重点是创建,创建零件是它的主要职责,组装顺序则不关心。
参考
《设计模式之禅》