1.为什么会出现Builder(构建者)模式?
在软件系统中,有时候面临着一个复杂对象的创建工作,复杂对象其通常由各个部分的子对象用一定的算法构成。由于需求的不断变化,这个复杂对象的各个部分经常面临着剧烈变化,但是它们组合在一起的算法却相对稳定。
如何应对这种变化?如何提供一种封装机制来隔离出复杂对象的各个部分的变化,从而保持系统中的稳定构建算法不随着需求的改变而改变?
2.Builder(构建者)模式定义
将一个复杂对象的构建与其表示相分离,使得同样的构建过程(稳定)可以创建不同的表示(变化)。举个易于理解的例子:建造一个房子,其基础的建造过程都是打地基、砌墙、装门窗等。但不同的设计师有不同的设计风格,因此最终建成的房子当然不一样。
3.Builder(构建者)模式基本结构
构建者模式中主要有以下几个角色:
抽象构建者:创建一个产品对象的各个部分指定的抽象接口;
具体构建者:实现抽象构建者的接口,实现各个部分的具体构造方法,并返回构建结果。
产品:具体的产品对象
指挥者:构建一个使用Builder接口的对象,安排复杂对象的构建过程,客户端程序一般只需要与指挥者交互,指定构建者类型,然后通过构造函数或setter方法将具体构建者对象传入指挥者。
4.Builder(构建者)模式实战
假设现在有一个场景:客户CurryCoder想要盖一个自家住的房子,他分别找到了同村的包工头小王和小李。两位包工头分别带来了自己的设计方案,再三考虑后CurryCoder选择了其中的一位设计方案。几个月后新房子盖好了,CurryCoder看着新房子很高兴。同时,他心里也在想这房子究竟是怎么建成的?包工头笑了笑说道:“这可是秘密哦~” 。
#include <iostream> #include <string> using namespace std; // 产品类House class House { public: House() {} void setRoof(string iRoof) { this->roof = iRoof; } void setWall(string iWall) { this->wall = iWall; } // 打印House信息 void printHouseInfo() { cout << "Roof: " << this->roof << endl; cout << "Wall: " << this->wall << endl; } private: string roof; string wall; }; // 抽象构建者AbstractBuilder class AbstractBuilder { public: AbstractBuilder() { house = new House(); } // 抽象方法 virtual void buildRoof() = 0; virtual void buildWall() = 0; virtual House *getHouse() = 0; House *house; }; // 具体构建者ConcreteBuilderA class ConcreteBuilderA : public AbstractBuilder { public: ConcreteBuilderA() { cout << "ConcreteBuilderA\n"; } // 具体实现方法 void buildRoof() { this->house->setRoof("Roof_A"); } void buildWall() { this->house->setWall("Wall_A"); } House *getHouse() { return this->house; } }; // 具体构建者ConcreteBuilderB class ConcreteBuilderB : public AbstractBuilder { public: ConcreteBuilderB() { cout << "ConcreteBuilderB\n"; } // 具体实现方法 void buildRoof() { this->house->setRoof("Roof_B"); } void buildWall() { this->house->setWall("Wall_B"); } House *getHouse() { return this->house; } }; // 指挥者Director class Director { public: Director() { } // 具体实现方法 void setBuilder(AbstractBuilder *iBuilder) { this->builder = iBuilder; } // 封装组装流程,返回构建结果 House *construct() { builder->buildRoof(); builder->buildWall(); return builder->getHouse(); } private: AbstractBuilder* builder; }; // 客户端程序 int main(){ // 抽象构建者 AbstractBuilder* builder; // 指挥者 Director* director = new Director(); // 产品 House* house; // 指定具体构建者A builder = new ConcreteBuilderA(); director->setBuilder(builder); house = director->construct(); house->printHouseInfo(); // 指定具体构建者B builder = new ConcreteBuilderB(); director->setBuilder(builder); house = director->construct(); house->printHouseInfo(); return 0; }
5.构建者(Builder)模式总结
从上面客户端代码可以看出:客户端只需要指定具体的构建者,并将它作为参数传递给指挥者,通过指挥者即可得到结果。客户端无需关心House的建造方法和具体流程。如果要更换构建风格,只需更换具体构建者即可,不同构建者之间并无任何关联,方便替换。从代码优化角度来看,其实可以不需要指挥者Director的角色,而直接把construct方法放入具体构建者当中。
优点:
构建者模式中,客户端不需要知道产品内部组成细节,将产品本身和产品创建过程分离,使用同样的创建过程可以创建不同的产品对象;不同建造者相互独立,并无任何挂链,方便替换。
缺点:
构建者模式所创建的产品一般具有较多共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用构建者模式,因此其使用范围受到一定的限制。
如果产品的内部变化复杂,可能会导致需要定义很多具体构建者类来实现这种变化,导致系统变得很庞大。