创意图案 - 5.Builder Patterns [Carrera Corporation] - 2. 说做就做
2.1 船模型
现在我们开始着手把路飞的海贼船桑尼号 和梅利号使用生成器模式键造出来。
- 一共需要三个生成器类,一共父类,两个子类
- 父类可以是一个抽象类,提供的建造函数都是虚函数
- 在两个生成器子类中,使用建造函数分别将
桑尼号
和梅利号
各个零部件造出来。
如果我们仔细分析,发现还需要解决另外一个问题,通过生成器得到了海贼船的各个零部件,这些零部件必须有一个载体,那就是海贼船对象
。因此,还需要提供一个或多个海贼船类。
因为桑尼号
和梅利号
这两艘的差别非常之巨大,所以我们定义两个海贼船类,代码如下:
// 桑尼号
class SunnyShip
{
public:
// 添加零件
void addParts(string name)
{
m_parts.push_back(name);
}
void showParts()
{
for (const auto& item : m_parts)
{
cout << item << " ";
}
cout << endl;
}
private:
vector<string> m_parts;
};
// 梅利号
class MerryShip
{
public:
// 组装
void assemble(string name, string parts)
{
m_patrs.insert(make_pair(name, parts));
}
void showParts()
{
for (const auto& item : m_patrs)
{
cout << item.first << ": " << item.second << " ";
}
cout << endl;
}
private:
map<string, string> m_patrs;
};
在上面的两个类中,通过一个字符串来代表某个零部件,为了使这两个类有区别SunnyShip 类
中使用vector 容器
存储数据,MerryShip 类
中使用map 容器
存储数据。
2.2 船生成器
虽然有海贼船类,但是这两个海贼船类并不造船,每艘船的零部件都是由他们对应的生成器类构建完成的,下面是生成器类的代码:
抽象生成器
// 生成器类
class ShipBuilder
{
public:
virtual void reset() = 0;
virtual void buildBody() = 0;
virtual void buildWeapon() = 0;
virtual void buildEngine() = 0;
virtual void buildInterior() = 0;
virtual ~ShipBuilder() {}
};
在这个抽象类中定义了建造海贼船所有零部件的方法,在这个类的子类中需要重写这些虚函数,分别完成桑尼号
和梅利号
零件的建造。
桑尼号生成器
// 桑尼号生成器
class SunnyBuilder : public ShipBuilder
{
public:
SunnyBuilder()
{
reset();
}
~SunnyBuilder()
{
if (m_sunny != nullptr)
{
delete m_sunny;
}
}
// 提供重置函数, 目的是能够使用生成器对象生成多个产品
void reset() override
{
m_sunny = new SunnyShip;
}
void buildBody() override
{
m_sunny->addParts("神树亚当的树干");
}
void buildWeapon() override
{
m_sunny->addParts("狮吼炮");
}
void buildEngine() override
{
m_sunny->addParts("可乐驱动");
}
void buildInterior() override
{
m_sunny->addParts("豪华内室精装");
}
SunnyShip* getSunny()
{
SunnyShip* ship = m_sunny;
m_sunny = nullptr;
return ship;
}
private:
SunnyShip* m_sunny = nullptr;
};
在这个生成器类中只要调用build 方法
,对应的零件就会被加载到SunnyShip 类的对象 m_sunny
中,当船被造好之后就可以通过SunnyShip* getSunny()
方法得到桑尼号的实例对象
,当这个对象地址被外部指针接管之后,当前生成器类就不会再维护其内存的释放了。如果想通过生成器对象建造第二艘桑尼号就可以调用这个类的reset()
方法,这样就得到了一个新的桑尼号对象,之后再调用相应的建造函数,这个对象就被初始化了。
梅利号生成器
// 梅利号生成器
class MerryBuilder : public ShipBuilder
{
public:
MerryBuilder()
{
reset();
}
~MerryBuilder()
{
if (m_merry != nullptr)
{
delete m_merry;
}
}
void reset() override
{
m_merry = new MerryShip;
}
void buildBody() override
{
m_merry->assemble("船体", "优质木材");
}
void buildWeapon() override
{
m_merry->assemble("武器", "四门大炮");
}
void buildEngine() override
{
m_merry->assemble("动力", "蒸汽机");
}
void buildInterior() override
{
m_merry->assemble("内室", "精装");
}
MerryShip* getMerry()
{
MerryShip* ship = m_merry;
m_merry = nullptr;
return ship;
}
private:
MerryShip* m_merry = nullptr;
};
梅利号的生成器和桑尼号的生成器内部做的事情是一样的,在此就不过多赘述了。
2.3 包工头
如果想要隐藏造船细节,就可以添加一个主管类,这个主管类就相当于一个包工头,脏活累活他都干了,我们看到的就是一个结果。
根据需求,桑尼号和梅利号分别有三个规格,简约型、标准型、豪华型
,根据不同的规格,有选择的调用生成器中不同的建造函数,就可以得到最终的成品了。
// 主管类
class Director
{
public:
void setBuilder(ShipBuilder* builder)
{
m_builder = builder;
}
// 简约型
void builderSimpleShip()
{
m_builder->buildBody();
m_builder->buildEngine();
}
// 标准型
void builderStandardShip()
{
builderSimpleShip();
m_builder->buildWeapon();
}
// 豪华型
void builderRegalShip()
{
builderStandardShip();
m_builder->buildInterior();
}
private:
ShipBuilder* m_builder = nullptr;
};
在使用主管类的时候,需要通过setBuilder(ShipBuilder* builder)
给它的对象传递一个生成器对象
,形参是父类指针,实参应该是子类对象,这样做的目的是为了实现多态,并且在这个地方这个函数是一个传入传出参数
。
下一篇: 海子去世后,罗一鹤开始燃烧