建造者设计模式
前言
建造者模式又被称呼为生成器模式,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
使用多个简单的对象一步一步构建成一个复杂的对象,有点像造房子一样一步步从地基做起到万丈高楼。注重的是new完对象之后的过程,而不是new 对象
简介
1、定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
2、主要作用:在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。(指挥者的作用)
3、如何使用:用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)
4、解决的问题:
(1)方便用户创建复杂的对象(不需要知道实现过程)
(2)代码复用性 & 封装性(将对象构建过程和细节进行封装 & 复用)
5、注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序,一般用来创建更为复杂的对象
建造者模式主要由几部分组成:产品(Product)建造者(Builder)指挥者(Director),以下面造电脑为例
产品类
class Computer {
private String CPU;
private String GPU;
private String MEMORY;
// 省略了getter setter方法
}
对于不同价位的电脑来说,他们的CPU,GPU,MEMORY都是不一样的,但是他们都有这些特性。
如果我要组装一台电脑,需要设置CPU,设置GPU,设置MEMORY等等,因此我们需要一个建造者来帮我们完成这类事情,首先定义一个电脑建造者接口,接口的主要目的增加规范性,以及符合设计模式原则,接口定义了子类必须设置CPU,设置GPU,设置MEMORY,并且返回组装好的Computer对象
建造者接口
interface ComputerBulider {
void setCPU();
void setGPU();
void setMEMORY();
Computer bulid();
}
建造者建造电脑类
接下来我们要开始造一台低配电脑,设置CPU,设置GPU,设置MEMORY等等
class LowComputer implements ComputerBulider{
private Computer computer = new Computer();
@Override
public void setCPU() {
computer.setCPU("LowComputer -> setCPU");
}
@Override
public void setGPU() {
computer.setGPU("LowComputer -> setGPU");
}
@Override
public void setMEMORY() {
computer.setMEMORY("LowComputer -> setMEMORY");
}
@Override
public Computer bulid() {
return computer;
}
}
再比如造一台中配电脑
class MiddleComputer implements ComputerBulider{
private Computer computer = new Computer();
@Override
public void setCPU() {
computer.setCPU("MiddleComputer -> setCPU");
}
@Override
public void setGPU() {
computer.setGPU("MiddleComputer -> setGPU");
}
@Override
public void setMEMORY() {
computer.setMEMORY("MiddleComputer -> setMEMORY");
}
@Override
public Computer bulid() {
return computer;
}
}
现在建造者类已经写完了,由于实现了ComputerBulider接口,所以扩展起来很方便
接下来我们想,关于怎么设置CPU,设置GPU,设置MEMORY的方法我们已经写好了,但是调用这些方法的先后顺序还没有确定,所以我们要把这些先后顺序封装起来,使得用户只要调用一个方法就可以自动的设置CPU,设置GPU,设置MEMORY,最后返回给用户一个组装好的电脑。笔者在准备加入指挥者的时候,想到是否可以用之前的模板方法设计模式来完成。为此,需要更改ComputerBulider这个接口bulid方法,使其变成一个默认方法,然后通过多态调用这个bulid方法也可以完成指挥者的功能。但事实上是行不通的,与其说行不通,也可以理解为条件很苛刻。
interface ComputerBulider {
void setCPU();
void setGPU();
void setMEMORY();
// 无法提供返回值,除非把ComputerBulider接口换成抽象类,并且提供一个属性是Computer的成员变量
default Computer bulid() {
this.setCPU();
this.setGPU();
this.setMEMORY();
}
}
首先,接口类无法实现default方法,因为无法给予返回值,而且一旦这样,ComputerBulider类既要承担设置CPU等等的具体实现,还需要设置这些方法的执行流程,违反了类的单一原则。因此,我们需要加入构造者这个类,自动的调用这些流程
指挥者
class Director {
public static Computer build(ComputerBulider computerBulider) {
computerBulider.setCPU();
computerBulider.setGPU();
computerBulider.setMEMORY();
Computer computer = computerBulider.bulid();
return computer;
}
}
指挥者类有一个静态方法,用户可以通过调用这个方法,从而很容易的得到一个组装好的Computer电脑实例,如果我想要得到一个低配电脑,那就可以Computer computer = Director.build(new LowComputer());
得到,如果我想得到一个中配电脑,那就可以Computer computer = Director.build(new MiddleComputer());
得到,从而屏蔽了内部设置CPU、GPU这些流程,增加了代码的封装性,但是我们从这也可以看出,设置CPU、GPU这些流程的顺序是固定了,无论是造一个低配电脑,还是中配电脑,都是先设置CPU,然后设置GPU,再设置MEMORY,当然,要修改其实也是可以的,那么就需要判断方法参数computerBulider是什么类型的,针对不同的类型设置不同的顺序
那么从这来看,模板设计模式和指挥者都是为了屏蔽方法调用的流程,那么他们之间又有什么区别呢,在这里我谈谈我的理解
模板设计模式和指挥者
首先,模板设计方式关于方法的调用流程是放在了父类,而子类可以通过设置一些方法来设置某个流程执不执行,比如isAlarm方法,并且父类和子类直接没有任何的成员变量传递,只是简单的调用方法,同时,父类可以很容易的在run方法中设置具体方法的执行步骤,可以控制方法执行流程顺序以及是否执行某个流程。如果我想要增加一个子类并且控制子类方法是否运行,只需要在子类中重写父类的钩子函数,而不需要改变父类代码
但是建造者设计模式就不一样,在建造者设计模式中,指挥者和建造者直接并不是子父类的关系,在指挥者中,想要修改方法的调用顺序比模板模式麻烦的多,如果新增一个组装高级电脑的类,想要控制它的顺序,就必须在指挥者模式中增加代码,违反开闭原则。
public abstract class HummerModel {
abstract protected void start();
abstract protected void stop();
abstract protected void alarm();
abstract protected void engineBoom();
public final void run() {
start();
if(this.isAlarm()) {
alarm();
}
engineBoom();
stop();
}
// 钩子函数
protected boolean isAlarm() {
return true;
}
}
// 子类
public class HummerCar1 extends HummerModel{
@Override
protected void start() {
System.out.println("template_method1.HummerCar1 start");
}
@Override
protected void stop() {
System.out.println("template_method1.HummerCar1 stop");
}
@Override
protected void alarm() {
System.out.println("template_method1.HummerCar1 alarm");
}
@Override
protected void engineBoom() {
System.out.println("template_method1.HummerCar1 engineBoom");
}
@Override
protected boolean isAlarm() {
return false;
}
}