建造者模式(Builder Pattern)又叫生成器模式,是一种对象构建模式,它可以将复杂对象的建造过程抽象出来(抽象类别),这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。建造者是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。模板方法模式是指在一个抽象类中公开定义了执行它的方法的方式/模板,它的子类可以按需要重写方法实现,这是一种行为型模式。这两个模式相似之处在于都需要抽象出共性行为,由子类去实现。本文将详述建造者模式和模板方法模式的概念及使用。
比如我们要组装一台电脑,需要组装的部分包括CPU、主板、屏幕、内存、磁盘、键盘、鼠标等,则会写出如下代码:
public class Computer {
private String cpu;
private String memory;
private String disk;
private String screen;
private String mouse;
private String mainBoard;
private String keyBoard;
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public String getMemory() {
return memory;
}
public void setMemory(String memory) {
this.memory = memory;
}
public String getDisk() {
return disk;
}
public void setDisk(String disk) {
this.disk = disk;
}
public String getScreen() {
return screen;
}
public void setScreen(String screen) {
this.screen = screen;
}
public String getMouse() {
return mouse;
}
public void setMouse(String mouse) {
this.mouse = mouse;
}
public String getMainBoard() {
return mainBoard;
}
public void setMainBoard(String mainBoard) {
this.mainBoard = mainBoard;
}
public String getKeyBoard() {
return keyBoard;
}
public void setKeyBoard(String keyBoard) {
this.keyBoard = keyBoard;
}
}
//创建对象方式如下:
Computer computer = new Computer();
computer.setCpu("M2");
computer.setScreen("mac screen");
......
这种方式写起来不够优雅,创建不同型号电脑时,需要创建不同Computer对象并挨个设置属性,编写了大量冗余代码,造成系统臃肿。
如果利用建造者模式来生成对象,则需要包含四个角色:Product(产品角色)、Builder(抽象建造者)、ConcreteBuilder(具体建造者)、Director(指挥者)。
1.Product(产品角色):一个具体的产品对象;
2.Builder(抽象建造者):创建一个Product对象的各个部件指定的接口/抽象类。
3.ConcreteBuilder(具体建造者):实现接口,构建和装配各个部件;
4.Director(指挥者):构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象。它的作用主要有两个,一是隔离客户与对象的生产过程,二是负责控制产品对象的生产过程。
利用建造者模式,定义产品角色:
public class Computer {
private String cpu;
private String memory;
private String disk;
private String screen;
private String mouse;
private String mainBoard;
private String keyBoard;
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public String getMemory() {
return memory;
}
public void setMemory(String memory) {
this.memory = memory;
}
public String getDisk() {
return disk;
}
public void setDisk(String disk) {
this.disk = disk;
}
public String getScreen() {
return screen;
}
public void setScreen(String screen) {
this.screen = screen;
}
public String getMouse() {
return mouse;
}
public void setMouse(String mouse) {
this.mouse = mouse;
}
public String getMainBoard() {
return mainBoard;
}
public void setMainBoard(String mainBoard) {
this.mainBoard = mainBoard;
}
public String getKeyBoard() {
return keyBoard;
}
public void setKeyBoard(String keyBoard) {
this.keyBoard = keyBoard;
}
}
定义抽象建造者:
public abstract class Builder {
abstract Builder buildScreen(String screen);
abstract Builder buildMouse(String mouse);
abstract Builder buildCpu(String cpu);
abstract Builder buildDisk(String disk);
abstract Builder buildMemory(String memory);
abstract Builder buildMainBoard(String mainBoard);
abstract Builder buildKeyBoard(String keyBoard);
abstract Computer build();
}
在定义具体的建造者MacBuilder,组装Mac电脑,LenovoBuilder(组装lenov电脑)、ASUSBuilder(组装华硕电脑)等,代码如下:
public class MacBuilder extends Builder {
private Computer computer = new Computer();
@Override
Builder buildScreen(String screen) {
computer.setScreen(screen);
System.out.println("组装屏幕" + screen + "成功");
return this;
}
@Override
Builder buildMouse(String mouse) {
computer.setMouse(mouse);
System.out.println("组装鼠标" + mouse + "成功");
return this;
}
@Override
Builder buildCpu(String cpu) {
computer.setCpu(cpu);
System.out.println("组装cpu" + cpu + "成功");
return this;
}
@Override
Builder buildDisk(String disk) {
computer.setDisk(disk);
System.out.println("组装disk" + disk + "成功");
return this;
}
@Override
Builder buildMemory(String memory) {
computer.setMemory(memory);
System.out.println("组装memory" + memory + "成功");
return this;
}
@Override
Builder buildMainBoard(String mainBoard) {
computer.setMainBoard(mainBoard);
System.out.println("组装mainBoard" + mainBoard + "成功");
return this;
}
@Override
Builder buildKeyBoard(String keyBoard) {
computer.setKeyBoard(keyBoard);
System.out.println("组装keyBoard" + keyBoard + "成功");
return this;
}
@Override
Computer build() {
System.out.println("电脑组装完成!");
return computer;
}
}
具体建造者完成,定义指挥者负责指挥建造:
public class Director {
Builder builder = null;
public Director(Builder builder) {
this.builder = builder;
}
public void processMain(String cpu, String disk, String memory, String mainBoard) {
builder.buildMemory(memory)
.buildMainBoard(mainBoard)
.buildCpu(cpu).buildDisk(disk);
}
public void processOther(String screen, String mouse, String keyBoard) {
builder.buildScreen(screen).buildKeyBoard(keyBoard).buildMouse(mouse);
}
}
测试代码如下:
@Slf4j
public class Test {
public static void main(String[] args) {
MacBuilder macBuilder = new MacBuilder();
Director director = new Director(macBuilder);
director.processMain("M2", "mac", "mac memory", "mac mainBoard");
Computer build = macBuilder.build();
log.info("build main:" + JSON.toJSONString(build));
director.processOther("mac Screen", "mac Mouse", "mac keyBoard");
Computer build1 = macBuilder.build();
log.info("build other:" + JSON.toJSONString(build));
}
}
测试结果如下:
上述两种方案的对比,建造者模式将对象的创建过程进行了解耦,如果对象属性多,构建发杂,建造者明显可以减少冗余代码的编写。对象的整个创建工作交给了构造器,符合单一原则。类图关系如下:
1.客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象;
2.每一个具体建造者都相对独立,而与其它的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同地具体建造者即可得到不同地产品对象;
3.可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程;
4.增加新的具体建造者无须修改原有类库的代码,指挥者针对抽象建造者类编程,系统扩展方便,符合“开闭原则”;
5.建造者模式所创建的产品一般具有较多共同点,其组成部分相似,如果产品之间差异性较大,则不适合使用建造者模式,因此其使用范围受到一定的限制;
6.如果产品的内部变化复杂,可能会导致需要定义较多具体建造者来实现这种变化,这会造成系统代码臃肿,此时需要考虑是否选择建造者模式。
抽象工厂模式实现对产品家族的创建,一个产品家族就是一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关系构建过程,只关心哪类产品由哪个工厂生产即可。建造者模式是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而生成产品。
模板方法模式(Template Method Pattern),又叫模板模式(Template
Pattern),在一个抽象类公开定义了执行它的方法的模板,它的子类可以按需要重写方法实现,调用以抽象类中定义的方式进行。简而言之,模板方法模式定义了一个操作中算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重新定义该算法的某些特定步骤。
举一个建房子的案例,建房子的流程是固定的:设计图纸、打地基、砌墙、结顶。可以定义一个模板方法来实现,如下:
public abstract class BuildHouseTemplate {
//钩子函数,预留前置可能执行的一些操作
void before() {
System.out.println("开始设计之前需要做的事情,比如准备资金,寻找场地");
}
//设计
void design() {
System.out.println("找设计师设计图纸");
}
//抽象方法,打地基,子类实现打地基的材料、方式
abstract void buildBase();
//抽象方法,砌墙
abstract void buildWall();
//结顶操作
void roofed() {
System.out.println("结顶盖瓦");
}
//钩子函数,预留后续可能执行的一些操作
void after() {
System.out.println("房屋结束之后要做的事情,比如放鞭炮庆祝,请吃席。。。");
}
//整体流程确定,不可更改,细节由具体子类根据场景实现
final void build() {
before();
design();
buildBase();
buildWall();
roofed();
after();
}
}
建造房屋有多种情形,这里分为建造高楼和普通房子,具体实现如下:
public class HighBuilding extends BuildHouseTemplate {
@Override
void before() {
System.out.println("建造高楼之前需要测量土地性质及地形,周边房屋、电缆情况,确定是否具有影响");
}
@Override
void buildBase() {
System.out.println("用钢筋混凝土打地基,还有加石头,地基要打深一定");
}
@Override
void buildWall() {
System.out.println("用玻璃来砌墙,采光好");
}
@Override
void after() {
System.out.println("高楼建造完成,需要放炮庆祝");
}
}
public class CommonBuilding extends BuildHouseTemplate{
@Override
void before() {
System.out.println("建造普通房屋,需要先准备资金和邀请人");
}
@Override
void buildBase() {
System.out.println("普通房子打地基,可以不用很深...");
}
@Override
void buildWall() {
System.out.println("用多孔砖砌墙,保温还减重...");
}
@Override
void after() {
System.out.println("普通房屋建造完成,请客吃饭");
}
}
测试类如下:
public class Test {
public static void main(String[] args) {
HighBuilding highBuilding = new HighBuilding();
highBuilding.build();
System.out.println("--------------------------------------");
CommonBuilding commonBuilding = new CommonBuilding();
commonBuilding.build();
}
}
测试结果如下:
模板方法一般类图(即案例类图)如下:
1.钩子方法一般默认不做任何事情,子类可以视情况决定是否覆盖;
2.模板方法一般添加final关键字进行修饰,保证子类中该方法不会被修改;
3.模板方法只存在于父类方法中,容易修改,一旦设计修改算法时,只需要修改父类即可;
4.实现了代码的可复用性,父类的一些方法可以直接被子类继承使用;
5.统一了算法,但也提供了灵活性,模板方法保证了算法结构的不变性;
6.模板方法的缺陷在于每个子类都要去实现父类,一定程度上会增加系统中类的个数。
1.建造者模式主要用来创建差异不大的对象;
2.模板方法模式则定义完整的实现/流程,不同子类根据情况进行改写具体实现,但算法实施步骤只能存在父类中,且不能被子类修改。
1.《设计模式-可复用面向对象软件的基础》-Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides
2.《可复用物联网Web3D框架的设计与实现》-程亮(知网)
3.https://www.bilibili.com/video/BV1G4411c7N4-尚硅谷设计模式