一、定义
将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式的基本思想是使用简单对象一步步构建一个复杂的对象。属于创建型模式。
建造者模式更关注各对象的组装过程,实现各个部件相互独立,并组装为复杂整体的作用。
二、建造者模式实例
每逢中秋,各商家都会生产出带有各种包装,用于各种场合的月饼产品。因为月饼产品除了月饼不可或缺外,由于使用场景不同,客户可能对产品的包装有不同的需求,这就导致“构建”月饼产品十分的复杂。
我们以“构建”这样的月饼产品为例。首先,我们假设我们的产品可能需要下列部件:
月饼(需要明确口味)、内包装、外包装、礼品袋。即具有这些成员的月饼类
class Mooncake
{
string _filling; //月饼口味
string _innerPackaging; //内包装
string _outerPackaging; //外包装
string _giftBags; //礼品袋包装
}
对于目标:构建出一件月饼产品,我们有如下方法
1、折叠式构造方法
定义一批构造函数,覆盖对必选和可选部件的组合,在构造时确认组装产品的部件
//折叠式构造方式
class Mooncake
{
public:
Mooncake(string filling) : _filling(filling) {}
Mooncake(string filling, string innerPackaging) : _filling(filling), _innerPackaging(innerPackaging) {}
Mooncake(string filling, string innerPackaging, string outerPackaging) : _filling(filling), _innerPackaging(innerPackaging), _outerPackaging(outerPackaging) {}
Mooncake(string filling, string innerPackaging, string outerPackaging, string giftBags) : _filling(filling), _innerPackaging(innerPackaging), _outerPackaging(outerPackaging), _giftBags(giftBags) {}
//...这还不止,还需要其他组合。例如_filling + _outerPackaging的。以下都省略
private:
string _filling; //月饼口味(必选)
string _innerPackaging; //内包装
string _outerPackaging; //外包装
string _giftBags; //礼品袋包装
};
int main()
{
//内包装为纸盒的五仁馅月饼产品
Mooncake *moonCake = new Mooncake("五仁",“纸盒”);
}
2、对外暴露部件设置,使各部件可以构造后再设置
class Mooncake
{
public:
Mooncake() :
_filling(""),
_innerPackaging(""),
_outerPackaging(""),
_giftBags("") {}
virtual ~Mooncake() {}
//內馅在子类实现确认
void SetFilling(string param)
{
_filling = param;
}
void SetInnerPackaging(string param)
{
_innerPackaging = param;
}
void SetOuterPackaging(string param)
{
_outerPackaging = param;
}
void SetGiftBags(string param)
{
_giftBags = param;
}
protected:
string _filling; //月饼口味
string _innerPackaging; //内包装
string _outerPackaging; //外包装
string _giftBags; //礼品袋包装
};
int main()
{
Mooncake *moonCake = new Mooncake();
moonCake -> SetFilling("五仁");
moonCake -> SetOuterPackaging("一次性方便袋");
}
但是上述2都有些显然的缺点。
使用方法1的构造函数要时刻注意传参的顺序是否错误,因为一旦传参太多,容易造成混乱,对可读性也不是很友好。而且一旦构建的可能组合太多,会需要大量构造函数。
使用方法2,虽然可以很方便地传入正确参数,将“部件”设置正确,也足够灵活。但是对象中的属性是分步设置的,在没有构建完成时,产品对象就已经对外暴露了,容易发生错误。
对于以上需求,我们可以用建造者模式解决。
3、建造者模式的实现
1)月饼产品类,提供对外设置属性的接口,但是和2方法不同
class Mooncake
{
public:
Mooncake() :
_filling(""),
_innerPackaging(""),
_outerPackaging(""),
_giftBags("") {}
virtual ~Mooncake() {}
//內馅在子类实现确认
void SetFilling(string param)
{
_filling = param;
}
void SetInnerPackaging(string param)
{
_innerPackaging = param;
}
void SetOuterPackaging(string param)
{
_outerPackaging = param;
}
void SetGiftBags(string param)
{
_giftBags = param;
}
void Introduce()
{
cout << "这款月饼是" << _filling << "馅的" << endl;
if ("" != _innerPackaging)
{
cout << "内包装采用" << _innerPackaging << endl;
}
else
{
cout << "无内包装" << endl;
}
if ("" != _outerPackaging)
{
cout << "外包装采用" << _outerPackaging << endl;
}
else
{
cout << "无外包装" << endl;
}
if ("" != _giftBags)
{
cout << "具有" << _giftBags << "的礼品袋" << endl;
}
cout << "您看是否符合您的需求。" << endl;
}
protected:
string _filling; //月饼口味
string _innerPackaging; //内包装
string _outerPackaging; //外包装
string _giftBags; //礼品袋包装
};
2)构建者抽象类,声明构建月饼产品的接口,用于规范化产品的构建,一般由子类具体实现构建方法
class Builder
{
public:
virtual ~Builder() {}
virtual void buildFilling() = 0; //构建內馅
virtual void buildInnerPackaging(string param) = 0; //构建内包装
virtual void buildOuterPackaging(string param) = 0; //构建外包装
virtual void buildGiftBags(string param) = 0; //构建礼品袋
virtual Mooncake* build() = 0; //对外提供构建的对象的指针
protected:
Mooncake *_moonCake; //待构建的对象指针
};
3)构建者具体类-五仁月饼构建者(filling属性可以确认),可以具体实现对目标对象的构建
class FiveNutsMooncakeBuilder :public Builder
{
public:
FiveNutsMooncakeBuilder()
{
_moonCake = new Mooncake();
}
~FiveNutsMooncakeBuilder()
{
if (nullptr != _moonCake)
{
delete _moonCake;
_moonCake = nullptr;
}
}
void buildFilling()
{
if (nullptr != _moonCake)
{
_moonCake->SetFilling("五仁");
}
}
void buildInnerPackaging(string param)
{
if (nullptr != _moonCake)
{
_moonCake->SetInnerPackaging(param);
}
}
void buildOuterPackaging(string param)
{
if (nullptr != _moonCake)
{
_moonCake->SetOuterPackaging(param);
}
}
void buildGiftBags(string param)
{
if (nullptr != _moonCake)
{
_moonCake->SetGiftBags(param);
}
}
Mooncake* build()
{
return _moonCake;
}
};
4)指挥者类,传入构建者对象,指挥构建者对产品的构建过程。在这个案例中,我写了两种组建方案:简单包装和精美包装。虽然里面的月饼可能是相同的产品,但是大家都知道这肯定是2种价格~
class Director
{
public:
Director(Builder* bd) :_builder(bd) {}
~Director()
{
if (nullptr != _builder)
{
delete _builder;
_builder = nullptr;
}
}
//简单包装的月饼产品
Mooncake* SimplePackaging()
{
if (nullptr != _builder)
{
_builder->buildFilling();
_builder->buildOuterPackaging("纸盒");
return _builder->build();
}
}
//精美包装的月饼产品
Mooncake* GiftPackaging()
{
if (nullptr != _builder)
{
_builder->buildFilling();
_builder->buildInnerPackaging("纸盒");
_builder->buildOuterPackaging("精美铁盒");
_builder->buildGiftBags("礼品袋");
return _builder->build();
}
}
private:
Builder* _builder;
};
5)使用
int main(int argc, char *argv[])
{
Builder *builder = new FiveNutsMooncakeBuilder();
Director *direstor = new Director(builder);
cout << "顾客A:" << endl;
//获取已经“建造”好的月饼产品
Mooncake* moonCakeA = direstor->GiftPackaging();
//输出介绍话术
moonCakeA->Introduce();
system("pause");
return 0;
}
6)这样构建的产品对象,方便扩展。假如想要增加别的月饼产品,比如增加什锦月饼。则我们就可以新增一个构建者。
class MixedMooncakeBuilder :public Builder
{
public:
MixedMooncakeBuilder()
{
_moonCake = new Mooncake();
}
~MixedMooncakeBuilder()
{
if (nullptr != _moonCake)
{
delete _moonCake;
_moonCake = nullptr;
}
}
void buildFilling()
{
if (nullptr != _moonCake)
{
_moonCake->SetFilling("什锦");
}
}
void buildInnerPackaging(string param)
{
if (nullptr != _moonCake)
{
_moonCake->SetInnerPackaging(param);
}
}
void buildOuterPackaging(string param)
{
if (nullptr != _moonCake)
{
_moonCake->SetOuterPackaging(param);
}
}
void buildGiftBags(string param)
{
if (nullptr != _moonCake)
{
_moonCake->SetGiftBags(param);
}
}
Mooncake* build()
{
return _moonCake;
}
};
三、总结
1、优缺点
该模式的主要优点如下:
1)封装性好,构建和表示分离;
2)扩展性好,各个具体的建造者相互独立,有利于系统的解耦;
3)客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险。
其缺点如下:
1)产品的组成部分必须相同,这限制了其使用范围。
2)如果产品的内部变化复杂,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。
2、建造者模式的适用场景
1、需要构建的对象相对复杂,具有多个属性,并且有些属性具有默认值;
2、相同的方法,不同的执行顺序,产生不同的结果;
3、多个部件,可以装配到一个对象中,但是不同组合产生的结果又不相同;
创建的产品具备复杂创建过程时,可以抽取出共性创建过程,然后交由指挥者类自定义创建流程,使得同样的创建行为可以(传入不同构建者)生产出不同的产品,分离了创建与表示,使创建产品的灵活性大大增加。
【参考&致谢】
设计模式之建造者(Builder)模式 | 菜鸟教程 (runoob.com)https://www.runoob.com/w3cnote/builder-pattern.html建造者模式(Bulider模式)详解 (biancheng.net)
http://c.biancheng.net/view/1354.html