设计模式(五)—— 建造者

127 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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(指导者):持有建造者接口引用的指导者类,指导建造者按一定的逻辑进行建造。对应本章例程中的工程总监类。

参考文档

  • 《秒懂设计模式》—— 刘韬