持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第25天,点击查看活动详情
概念
建造者模式(Builder)又称为生成器模式,主要用于对复杂对象的构建、初始化,它可以将多个简单的组件对象按顺序一步步组装起来,最终构造成一个复杂的成品对象。
建造者模式所构建的对象一定是庞大而复杂的,并且一定是按照既定的制造工序将组件组装起来的,例如计算机、汽车、建筑物等。
与工厂系列模式不同的是,建造者模式的主要目的在于把 烦琐的构建过程从不同对象中抽离出来,使其脱离并独立于产品类与工厂类,最终实现用同一套标准的制造工序能够产出不同的产品。
实例演示
注:本文内容参考 《秒懂设计模式》一书,本文对其做了概括凝练,主要是为了自身学习使用,如果无法理解,建议查看原书。
1. 建筑物组件构造
既然是建造者,我们就以建筑物建造为例来介绍演示。首先,建筑物本身应该由多个组件组成,且各组件按一定工序建造,缺一不可,建筑物的组件建造是相当复杂的,为了简化其数据模型,我们将组成建筑物的模块归纳为3个组件,分别是地基、墙体、屋顶,将它们组装起来就能形成一座建筑物,代码如下所示:
public class Building {
// 用List来模拟建筑物组件的组装
private List<String> buildingComponents = new ArrayList<>();
public void setBasement(String basement) {// 地基
this.buildingComponents.add(basement);
}
public void setWall(String wall) {// 墙体
this.buildingComponents.add(wall);
}
public void setRoof(String roof) {// 屋顶
this.buildingComponents.add(roof);
}
@Override
public String toString() {
String buildingStr = "";
for (int i = buildingComponents.size() - 1; i >= 0; i--) {
buildingStr += buildingComponents.get(i);
}
return buildingStr;
}
}
在上述代码中,我们分别定义各组件对应的建造方法(set 方法),其中可以看到我们用字符串对象 String来模拟各个组件对象。最后为了直观地看到建筑物的建造情况,我们重写了 toString() 方法,按从大到小的组件索引顺序组装各组件,后组装的组件应先展示出来,如屋顶应该首先输出,以此类推。
2. 确立施工标准(建造者接口)
既然一个房屋的组件我们已经确认好了,包括地基、墙体、房屋,那么我们应该招标建筑方了,建筑方至少具备实现三大组件能力,于是施工标准就确认了(即我们建造者接口应该实现哪些功能),施工标准如下:
public interface Builder {
public void buildBasement();
public void buildWall();
public void buildRoof();
public Building getBuilding();
}
3. 开发商施工(接口实现)
在我们公布招标标准后,假设有两方中标,分别为别墅、多层公寓,他们的实现方法如下。
public class HouseBuilder implements Builder {
private Building house;
public HouseBuilder() {
house = new Building();
}
@Override
public void buildBasement() {
System.out.println("挖土方,部署管道、线缆,水泥加固,搭建围墙、花园。");
house.setBasement("╬╬╬╬╬╬╬╬\n");
}
@Override
public void buildWall() {
System.out.println("搭建木质框架,石膏板封墙并粉饰内外墙。");
house.setWall("|田|田 田|\n");
}
@Override
public void buildRoof() {
System.out.println("建造木质屋顶、阁楼,安装烟囱,做好防水。");
house.setRoof("╱◥███◣\n");
}
@Override
public Building getBuilding() {
return house;
}
}
public class ApartmentBuilder implements Builder {
private Building apartment;
public ApartmentBuilder() {
apartment = new Building();
}
@Override
public void buildBasement() {
System.out.println("深挖地基,修建地下车库,部署管道、线缆、风道。");
apartment.setBasement("╚═════════╝\n");
}
@Override
public void buildWall() {
System.out.println("搭建多层建筑框架,建造电梯井,钢筋混凝土浇灌。");
for (int i = 0; i < 8; i++) {// 此处假设固定8层
apartment.setWall("║ □ □ □ □ ║\n");
}
}
@Override
public void buildRoof() {
System.out.println("封顶,部署通风井,做防水层,保温层。");
apartment.setRoof("╔═════════╗\n");
}
@Override
public Building getBuilding() {
return apartment;
}
}
4. 工程总监(指导者)
虽然上面的施工方看起来施工质量非常的好,但是开发商还是不放心,为了解决这个问题,开发商又招聘了一个专业的工程总监来做监理工作,他亲临施工现场指导施工,并把控整个施工流程,代码如下:
public class Director {//工程总监
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public void setBuilder(Builder builder) {
this.builder = builder;
}
public Building direct() {
System.out.println("=====工程项目启动=====");
// 第一步,打好地基
builder.buildBasement();
// 第二步,建造框架、墙体
builder.buildWall();
// 第三步,封顶
builder.buildRoof();
System.out.println("=====工程项目竣工=====");
return builder.getBuilding();
}
}
从上面的代码可以看出,工程总监的作用是从宏观上管理项目并指导整个施工队的建造流程,依次调用施工方的打地基方法 buildBasement()、建造墙体方法 buildWall() 及建筑物封顶方法 buildRoof(),保证了建筑物自下而上的建造工序。
而施工方是从外部注入的,所以工程总监并不关心是哪个施工方来造房子,更不关心施工方有什么样的建造工艺,但他能保证对施工工序的绝对把控,也就是说,工程总监只控制施工流程。
5. 项目实施(构造客户端)
至此,招标结束,一切准备就绪,我们就可以启动项目了,即构造客户端,客户端代码如下:
public class Client {
public static void main(String[] args) {
//组建别墅施工队
Director director = new Director(new HouseBuilder());
System.out.println(director.direct());
//替换施工队,建造公寓
director.setBuilder(new ApartmentBuilder());
System.out.println(director.direct());
}
}
最后项目顺利完工。
总结
看完了建造者模式工作的离职之后,我们再来看看文章开头的话,相信就能更好的理解了。
建造者模式的主要目的在于把 烦琐的构建过程从不同对象中抽离出来,使其脱离并独立于产品类与工厂类,最终实现用同一套标准的制造工序能够产出不同的产品。
建造者模式的各角色定义如下:
Product(产品):复杂的产品类,构建过程相对复杂,需要其他组件组装而成。对应本章例程中的建筑物类。Builder(建造者):建造者接口,定义了构成产品的各个组件的构建标准,通常有多个步骤。对应本章例程中的施工方接口。ConcreteBuilder(建造者实现):具体的建造者实现类,可以有多种实现,负责产品的组装但不包含整体建造逻辑。对应本章例程中的别墅施工方类与多层公寓施工方类。Director(指导者):持有建造者接口引用的指导者类,指导建造者按一定的逻辑进行建造。对应本章例程中的工程总监类。
参考文档
- 《秒懂设计模式》—— 刘韬