目录
1.变化是永恒的
在模板方法模式中,我们的模板方法是按照固定的顺序进行执行的。假如我们顺序是可以调整的,有些方法可能执行,有些方法可能不执行,又该如何设计呢?我们以制造汽车的过程为例,假如汽车形式分为启动、停止、鸣喇叭和引擎声等几个过程,而不同品牌的汽车,执行过程又不尽相同,此时我们可以在汽车模型类CarModel中定义一个setSequence方法,车辆模型的这几个动作如何排布,是定义在这个ArrayList中的,然后run()方法根据sequence定义的顺序完成指定的顺序动作。CarModel的源代码定义如下:
import java.util.ArrayList;
import java.util.List;
/**
* @author: martin
* @date: 2019/9/22 20:38
* @description:
*/
public abstract class CarModel {
//存放各个基本方法执行的顺序
private List<String> sequence = new ArrayList<>();
//启动开始跑
protected abstract void start();
//停下来
protected abstract void stop();
//喇叭鸣笛
protected abstract void alarm();
//引擎轰轰响
protected abstract void engineBoom();
//汽车运行
public final void run() {
for (String actionName : sequence) {
if (actionName.equals("start")) {
this.start();
} else if (actionName.equals("stop")) {
this.stop();
} else if (actionName.equals("alarm")) {
this.alarm();
} else if (actionName.equals("engine")) {
this.engineBoom();
}
}
}
protected void setSequence(List<String> sequence) {
this.sequence = sequence;
}
}
CarModel的设计原理是这样的,setSequence方法允许客户自己设置一个顺序。因此,对于一个具体的模型永远都是固定的,但是对N多个模型个模型就是动态的了。在子类中实现父类的基本方法,run()方法读取sequence,然后遍历sequence中的字符串,哪个字符串在前,就先执行哪个方法。
奔驰模型代码:
public class BenzModel extends CarModel {
@Override
protected void start() {
System.out.println("奔驰车启动");
}
@Override
protected void stop() {
System.out.println("奔驰车停车");
}
@Override
protected void alarm() {
System.out.println("奔驰车鸣笛");
}
@Override
protected void engineBoom() {
System.out.println("奔驰车引擎");
}
}
宝马模型:
public class BMWModel extends CarModel {
@Override
protected void start() {
System.out.println("宝马车启动");
}
@Override
protected void stop() {
System.out.println("宝马车停车");
}
@Override
protected void alarm() {
System.out.println("宝马车鸣笛");
}
@Override
protected void engineBoom() {
System.out.println("宝马车引擎");
}
}
两个产品的实现类都完成了,我们来模拟一下生产的过程:
public class Client {
public static void main(String[] args) {
BenzModel benzModel = new BenzModel();
List<String> sequence = new ArrayList<>();
sequence.add("engine");
sequence.add("start");
sequence.add("stop");
benzModel.setSequence(sequence);
benzModel.run();
}
}
输出结果为:
奔驰车引擎
奔驰车启动
奔驰车停车
这样我们就组装了一辆奔驰车。但是按照现在的设计方法,假如我们再要一个宝马模型只要启动和停止,其他的都不要,我们就需要在场景类Client中创建sequence,然后再run。如果又有新的模型,我们就一个一个地来写场景类来满足吗?为了解决这种无休止的新增代码,我们可以为每一种模型产品定义一个建造者,需要什么样的顺序直接告诉建造者,由建造者来建造,实现的类图如下:
上图中增加了一个CarBuilder抽象类,由它来组装各个车模,要什么类型什么顺序的车辆模型都由相关的子类完成。代码如下:
public abstract class CarBuilder {
//建造一个模型的组装顺序
public abstract void setSequence(ArrayList<String> sequence);
//顺序设置完成后,就可以直接拿到车辆模型
public abstract CarModel getCarModel();
}
public class BenzBuilder extends CarBuilder {
private BenzModel benzModel = new BenzModel();
@Override
public void setSequence(ArrayList<String> sequence) {
this.benzModel.setSequence(sequence);
}
@Override
public CarModel getCarModel() {
return this.benzModel;
}
}
public class BMWBuilder extends CarBuilder {
private BMWModel bmwModel = new BMWModel();
@Override
public void setSequence(ArrayList<String> sequence) {
this.bmwModel.setSequence(sequence);
}
@Override
public CarModel getCarModel() {
return this.bmwModel;
}
}
场景类实现如下:
public class Client {
public static void main(String[] args) {
ArrayList<String> sequence = new ArrayList<>();
sequence.add("engine");
sequence.add("start");
sequence.add("stop");
BenzBuilder benzBuilder = new BenzBuilder();
benzBuilder.setSequence(sequence);
BenzModel benzModel = (BenzModel) benzBuilder.getCarModel();
benzModel.run();
BMWBuilder bmwBuilder = new BMWBuilder();
bmwBuilder.setSequence(sequence);
BMWModel bmwModel = (BMWModel) bmwBuilder.getCarModel();
bmwModel.run();
}
}
奔驰车引擎
奔驰车启动
奔驰车停车
宝马车引擎
宝马车启动
宝马车停车
这样我们就可以生产出相同顺序的宝马车和奔驰车了,实现的代码也比直接访问产品类简单了很多。
建造者模式也叫做生成器模式,定义如下:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
2. 建造者模式的应用
2.1 优点
- 封装性:使用建造者模式可以使得客户端不必知道产品内部组成的细节。
- 建造者独立,容易扩展:BenzBuilder和BMWBuilder是相互独立的,对系统的扩展非常有利。
- 便于控制细节:由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。
2.2 使用场景
- 相同的方法,不同的执行顺序,产生不同的事件结果时,可以采用建造者模式。
- 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果有不相同时,则可以使用该模式。
- 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式非常合适。
建造者模式最主要的功能是基本方法调用顺序的安排,也就是这些基本方法已经实现了,关注的是零件类型和装配工艺(顺序)。而工厂方法则重点是创建,创建零件是它的主要职责,组装顺序则不是它关心的。两者虽然同为创建类模式,但是注重点不同。