建造者模式(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();@OverrideBuilder buildScreen(String screen) {computer.setScreen(screen);System.out.println("组装屏幕" + screen + "成功");return this;}@OverrideBuilder buildMouse(String mouse) {computer.setMouse(mouse);System.out.println("组装鼠标" + mouse + "成功");return this;}@OverrideBuilder buildCpu(String cpu) {computer.setCpu(cpu);System.out.println("组装cpu" + cpu + "成功");return this;}@OverrideBuilder buildDisk(String disk) {computer.setDisk(disk);System.out.println("组装disk" + disk + "成功");return this;}@OverrideBuilder buildMemory(String memory) {computer.setMemory(memory);System.out.println("组装memory" + memory + "成功");return this;}@OverrideBuilder buildMainBoard(String mainBoard) {computer.setMainBoard(mainBoard);System.out.println("组装mainBoard" + mainBoard + "成功");return this;}@OverrideBuilder buildKeyBoard(String keyBoard) {computer.setKeyBoard(keyBoard);System.out.println("组装keyBoard" + keyBoard + "成功");return this;}@OverrideComputer 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 {@Overridevoid before() {System.out.println("建造高楼之前需要测量土地性质及地形,周边房屋、电缆情况,确定是否具有影响");}@Overridevoid buildBase() {System.out.println("用钢筋混凝土打地基,还有加石头,地基要打深一定");}@Overridevoid buildWall() {System.out.println("用玻璃来砌墙,采光好");}@Overridevoid after() {System.out.println("高楼建造完成,需要放炮庆祝");}
}public class CommonBuilding extends BuildHouseTemplate{@Overridevoid before() {System.out.println("建造普通房屋,需要先准备资金和邀请人");}@Overridevoid buildBase() {System.out.println("普通房子打地基,可以不用很深...");}@Overridevoid buildWall() {System.out.println("用多孔砖砌墙,保温还减重...");}@Overridevoid 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-尚硅谷设计模式