1. 说明
什么是生成器模式
生成器模式是创建型模式的一种,指使用多个实例通过一定的步骤来生成所需的类的实例,这里的步骤是相同的,但是通过传递不同的参数来达到生成不同实例的目的。
意图
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
使用的场合
在以下情况使用Builder模式:
- 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时;
- 当构造过程必须允许被构造的对象有不同的表示时。
效果
- 它使你可以改变一个产品的内部表示。Builder对象提供给导向器一个构造产品的抽象接口。该接口使得生成器可以隐藏这个产品的表示和内部结构。它同时也隐藏了该产品是如何装配的。因为产品是通过抽象接口构造的,你在改变该产品的内部表示是所要做的只是定义一个新的生成器。
- 它将构造代码和表示代码分开。Builder模式通过封装一个复杂对象的创建和表示方式提高了对象的模块性。客户不需要知道定义产品内部结构的类的所有信息,这些类是不出现在Builder接口中的。
- 它使你可对构造过程进行更精细的控制。Builder模式与一下子就生成产品的创建型模式不同,它是在导向器的控制下一步一步构造产品的。仅当该产品完成时导向器才从生成器中取回它。因此Builder接口相比其他创建型模式能更好地反映产品的构造过程。这使你可以更精细地控制构建过程,从而能更精细地控制所得产品的内部结构。
优点
- 建造者独立,易扩展。
- 便于控制细节风险。
缺点
- 产品必须有共同点,范围有限制。
- 如内部变化复杂,会有很多的建造类。
2. 示例
2.1 框架
我们假设一个快餐店的商业案例,其中,一个典型的套餐可以是一个汉堡(Burger)和一杯冷饮(Cold drink)。汉堡(Burger)可以是素食汉堡(Veg Burger)或鸡肉汉堡(Chicken Burger),它们是包在纸盒中。冷饮(Cold drink)可以是可口可乐(coke)或百事可乐(pepsi),它们是装在瓶子中。
我们将创建一个表示食物条目(比如汉堡和冷饮)的 Item 接口和实现 Item 接口的实体类,以及一个表示食物包装的 Packing 接口和实现 Packing 接口的实体类,汉堡是包在纸盒中,冷饮是装在瓶子中。
然后我们创建一个 Meal 类,通过结Item 来创建不同类型的 Meal 对象的 MealBuilder。Demo 使用 MealBuilder 来创建一个 Meal。
结构图如下:
2.2 代码实现
2.2.1 抽象接口类
Packing.h
#ifndef __PACKING__
#define __PACKING__
class Packing {
public:
virtual string pack() = 0;
};
#endif
Packing 是食品包装的抽象接口;
Item.h
#ifndef __ITEM__
#define __ITEM__
class Packing {
public:
virtual string pack() = 0;
}
class Item{
public:
virtual string name() = 0;
virtual Packing *packing() = 0;
virtual float price() = 0;
};
#endif
Item 则是食物条目的抽象接口类;
2.2.2 实现Packing接口的实体类
Wrapper.h
#ifndef __WRAPPER__
#define __WRAPPER__
#include "Packing.h"
class Wrapper : public Packing{
public:
string pack() {return "Wrapper";};
};
#endif
Bottle.h
#ifndef __BOTTLE__
#define __BOTTLE__
#include "Packing.h"
class Bottle : public Packing{
public:
string pack() {return "Bottle";};
};
#endif
2.2.3 创建实现 Item 接口的抽象类,该类提供了默认的功能
Burger.h
#ifndef __BURGER__
#define __BURGER__
#include "Item.h"
#include "Packing.h"
#include "Wrapper.h"
#include "Bottle.h"
class Burger : public Item{
public:
Packing *packing() {return new Wrapper();};
};
class VegBurger : public Burger{
public:
float price() {return 25.0;};
string name() {return "Veg Burger";};
};
class ChickenBurger : public Burger{
public:
float price() {return 50.0;};
string name() {return "ChickenBurger";};
};
#endif
Drink.h
#ifndef __DRINK__
#define __DRINK__
#include "Item.h"
#include "Packing.h"
#include "Wrapper.h"
#include "Bottle.h"
class Drink : public Item{
public:
Packing *packing() {return new Bottle();};
};
class Coke : public Drink{
public:
float price() {return 30.0;};
string name() {return "Coke";};
};
class Pepsi : public Drink{
public:
float price() {return 35.0;};
string name() {return "Pepsi";};
};
#endif
2.2.4 创建一个 Meal 类,带有上面定义的 Item 对象
Meal.h
#ifndef __MEAL__
#define __MEAL__
#include <vector>
#include "Item.h"
using namespace std;
class Meal{
public:
Meal() {items = new vector<Item*>;};
void AddItem(Item *it) {items->push_back(it);};
float GetCost();
void ShowItem();
private:
vector<Item*> *items;
};
#endif
Meal.cpp
#include <iostream>
#include "Meal.h"
#include "Burger.h"
#include "Drink.h"
float Meal::GetCost()
{
float cost = 0;
auto item = items->begin();
while(item != items->end()){
cost += (*item)->price();
item++;
}
return cost;
}
void Meal::ShowItem()
{
auto item = items->begin();
while(item != items->end()){
std::cout << "Item: " << (*item)->name() << ", Packing: " << (*item)->packing()->pack() << ", Price: " << (*item)->price() << std::endl;
item++;
}
}
2.2.5 创建一个 MealBuilder 类,实际的 builder 类负责创建 Meal 对象
MealBuilder.h
#ifndef __MEALBUILDER__
#define __MEALBUILDER__
#include "Meal.h"
class MealBuilder{
public:
Meal *PrepareVegMeal();
Meal *PrepareNonVegMeal();
};
#endif
MealBuilder.cpp
#include "MealBuilder.h"
#include "Burger.h"
#include "Drink.h"
Meal *MealBuilder::PrepareVegMeal()
{
Item *vb = (Item *)new VegBurger();
Item *coke = (Item *)new Coke();
Meal *meal = new Meal();
meal->AddItem(vb);
meal->AddItem(coke);
return meal;
}
Meal *MealBuilder::PrepareNonVegMeal()
{
Item *cb = (Item *)new ChickenBurger();
Item *pp = (Item *)new Pepsi();
Meal *meal = new Meal();
meal->AddItem(cb);
meal->AddItem(pp);
return meal;
}
2.2.6 Demo 使用 MealBuider 来演示建造者模式(Builder Pattern)
Demo.cpp
#include "MealBuilder.h"
int main(int argc, char **argv)
{
MealBuilder *mb = new MealBuilder();
Meal *vegm = mb->PrepareVegMeal();
vegm->ShowItem();
std::cout << "VegMeal price: " << vegm->GetCost() << std::endl;
Meal *nvegm = mb->PrepareNonVegMeal();
nvegm->ShowItem();
std::cout << "VegMeal price: " << nvegm->GetCost() << std::endl;
return 0;
}
2.3 编译运行
g++ *.cpp
./a.out
2.4 结果
Item: Veg Burger, Packing: Wrapper, Price: 25
Item: Coke, Packing: Bottle, Price: 30
VegMeal price: 55
Item: ChickenBurger, Packing: Wrapper, Price: 50
Item: Pepsi, Packing: Bottle, Price: 35
VegMeal price: 85
3. 总结
Builder模式与Abstract Factory或Factory模式相似,都可以创建复杂对象,但是不同之处在于Builder模式着重于一步步构造一个复杂对象,在最后一步返回产品,强调构造细节,而后两者着重于多个系列的产品一次生成,通常产品是立即返回的。
参考资料
《设计模式 可复用面向对象软件的基础》
https://www.runoob.com/design-pattern/builder-pattern.html