这一次要根据需求,在run()方法中间执行不同顺序的任务。
类图比较简单,在CarModel中定义了一个setSequence方法,车辆模型的几个动作要如何排布,在ArrayList中定义。然后run()方法根据sequence定义的顺序完成指定的顺序动作。
车辆模型的抽象类
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 (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();
}
}
}
// 把传递过来的值传递到类内
final public void setSequence(ArrayList sequence) {
this.sequence = sequence;
}
}
CarModel的设计原理,setSequence方法允许客户自己设置一个顺序。对于一个具体的模型永远是固定的,但是对N多个模型就是动态的。在子类中实现父类的基本方法,run()方法读取sequence,然后遍历sequence中的字符串,哪个字符串在先,就先执行哪个方法
奔驰车模型
public class BenzModel extends CarModel {
protected void alarm() {
System.out.println("奔驰车的喇叭声音是这个样子的...");
}
protected void engineBoom() {
System.out.println("奔驰车的引擎是这个声音的...");
}
protected void start() {
System.out.println("奔驰车跑起来是这个样子的...");
}
protected void stop() {
System.out.println("奔驰车应该这样停车");
}
}
模拟XX公司要求,跑奔驰模型代码
public class Client {
public static void main(String[] args) {
BenzModel benz = new BenzModel();
ArrayList<String> sequence = new ArrayList<String>();
sequence.add("engine boom");
sequence.add("start");
sequence.add("stop");
// 把这个顺序赋予奔驰车
benz.setSequence(sequence);
benz.run();
}
}
这样组装了一辆奔驰车,满足了XX公司的要求。但想想我们的需求,汽车动作执行顺序是要能够随意调整的。我们有许多需求需要满足,一个一个写场景类肯定不可能。我们要为每种模型产品定义一个建造者,要什么顺序直接告诉建造者,由建造者创建。于是有了新类图
增加了一个CarBuilder抽象类,由它来组装各个车模,要什么类型什么顺序的车辆模型,都由相关的子类完成。抽象汽车组装者
public abstract class CarBuilder {
// 建造一个模型,给一个顺序
public abstract void setSequence(ArrayList<String> sequence);
// 设置完顺序后,可以直接拿到这个车辆模型
public abstract CarModel getCarModel();
}
奔驰车组装者
public class BenzBuilder extends CarBuilder {
private BenzModel benz = new BenzModel();
public void setSequence(ArrayList<String> sequence) {
this.benz.setSequence(sequence);
}
public CarModel getCarModel() {
return this.benz;
}
}
宝马车组装者
public class BMWBuilder extends CarBuilder {
private BMWModel bmw = new BMWModel();
public void setSequence(ArrayList<String> sequence) {
this.bmw.setSequence(sequence);
}
public CarModel getCarModel() {
return this.bmw;
}
}
修改后的场景类
public class Client {
public static void main(String[] args) {
ArrayList<String> sequence = new ArrayList<String>();
sequence.add("engine boom");
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();
}
}
现在有了同样顺序的宝马和奔驰车,比刚开始直接访问产品类简单了很多。
但是现在不同过程排列组合非常多,要什么顺序的车模就必须生成什么顺序的车模。我们不可能预知他们要什么顺序的模型。因此找一个类来指挥各个事件的先后顺序,然后为每种顺序指定一个代码,说一种立刻就生产一种
类图复杂了,还是比较简单,增加了一个Director类,负责按照指定的顺序生产模型
-
getABenzModel方法
组建出A型号的奔驰车,过程为启动、停止
-
getBBenzModel方法
组建出B型号的奔驰车,过程为发动引擎,启动,停车
-
getCBMWModel方法
组建出C型号的宝马车,其过程为先喇叭叫一下,启动,停车
-
getDBMWModel方法
组建出D型号的宝马车,其过程为启动
Director类代码
public class Director {
private ArrayList<String> sequence = new ArrayList<>();
private BenzBuilder benzBuilder = new BenzBuilder();
private BMWBuilder bmwBuilder = new BMWBuilder();
// A类型奔驰车模型
public BenzModel getABenzModel() {
// 一定要注意清理场景
this.sequence.clear();
this.sequence.add("start");
this.sequence.add("stop");
this.benzBuilder.setSequence(this.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.benzBuilder.setSequence(this.sequence);
return (BMWModel) this.bmwBuilder.getCarModel();
}
// D类型宝马车模型
public BMWModel getDBMWModel() {
this.sequence.clear();
this.sequence.add("start");
this.benzBuilder.setSequence(this.sequence);
return (BMWModel) this.bmwBuilder.getCarModel();
}
}
程序中有很多this调用。如果要调类中的成员变量或方法,需要在前面加上this关键字,不加也能正常跑起来,但是不清晰。加上this关键字,就是要调用本类中的成员变量和方法,而不是本方法中的一个变量,还有super方法也是一样,是调用父类的成员边浪或方法,那就加上关键字,不要省略。
注意:上面每个方法都有一个this.sequence.clear()。系统分析师或技术经理一定要告诉项目成员,ArrayList和HashMap如果定义成员类的成员变量,那你在方法中的调用一定要clear,以防止数据混乱。
导演类来创建奔驰车
public class _1Client {
public static void main(String[] args) {
Director director = new Director();
// 10辆A类型奔驰车
for (int i = 0; i < 10; i++) {
director.getABenzModel().run();
}
// 10辆B类型奔驰车
for (int i = 0; i < 10; i++) {
director.getBBenzModel().run();
}
// 10辆C类型宝马车
for (int i = 0; i < 10; i++) {
director.getCBMWModel().run();
}
}
}
建造者模式(Builder Pattern)也叫做生成器模式,定义如下
Seperate the construction of a complex object from its representation so that the same construction process can create different representations.
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式通用类图
建造者模式的4个角色
-
Product产品类
通常是实现了模板方法模式,也就是模板方法和基本方法
-
Builder抽象建造者
规范产品的组建,一般是由子类实现例子中的CarBuilder就属于抽象建造者
-
ConcreteBuilder具体建造者
实现抽象类定义的所有方法,并且返回一个组建好的对象
-
Director导演类
负责安排已有的模块顺序,告诉Builder开始建造
建造者模式的通用源代码也比较简单,先看Product类,通常是一个组合或继承(如模板方法模式)产生的类
产品类
public class Product {
public void doSomething() {
// 独立业务处理
}
}
抽象建造者
public abstract class Builder {
// 设置产品的不同部分,以获得不同的产品
public abstract void setPart();
// 建造产品
public abstract Product buildProduct();
}
其中setPart方法是零件的配置,什么是零件?其他的对象,获得一个不同零件或者不同的装配顺序就可能产生不同的产品
具体建造者
public class ConcreteProduct extends Builder {
private Product product = new Product();
// 设置产品零件
public void setPart() {
// 产品类内的逻辑处理
}
// 组建一个产品
public Product buildProduct() {
return product;
}
}
导演类
public class Director {
private Builder builder = new ConcreteProduct();
// 构建不同的产品
public Product getAProduct() {
builder.setPart();
// 设置不同的零件,产生不同的产品
return builder.buildProduct();
}
}
导演类起到封装的作用,避免高层模块深入建造者内部的实现类。建造者模式比较庞大时,导演类可以有多个。
-
封装性
使用建造者模式可以使客户端不必知道产品内部组成的细节,我们不需要关心每一个具体的模型内部如何实现,产生的对象类型就是CarModel
-
建造者独立,容易扩展
BenzBuilder和BMWBuilder是相互独立的,对系统的扩展非常有利
-
便于控制细节风险
由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他模块产生任何影响
- 相同的方法,不同的执行顺序,产生不同的事件结果时,可以采用建造者模式
- 多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时,则可以使用该模式
- 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式非常合适
- 在对象创建过程中会使用到系统中的一些其他对象,这些对象在产品对象的创建过程中不易得到时,也可以采用建造者模式封装该对象的创建过程。该种场景只能是一个补偿方法,因为一个对象不容易获得,而在设计阶段竟然没有发觉,而要通过创建者模式柔化创建过程,本身已经违反了设计的最初目标
建造者模式关注的是零件类型和装配工艺(顺序),这是它与工厂方法模式最大不同的地方,虽同为创建类模式,但注重点不同。
我们在汽车模型制造的例子中已经对建造者模式进行了扩展,引入了模板方法模式。建造者模式中还有一个角色,零件。建造者通过零件的组装来创建对象,组装顺序不同对象效能也不同,这才是建造者模式要表达的核心意义。引入模板方法模式是一个非常简单而有效的方法。
建造者模式和工厂方法模式非常相似,但要记住:建造者模式最主要的功能是基本方法的调用顺序安排,基本方法已经实现,顺序不同产生的对象也不同;工厂方法重点是创建,创建零件是它的主要职责,组装顺序不在考虑范围内。
在使用建造者模式的时候考虑一下模板方法模式,不独立思考一个模式。