通过“对象创建” 模式绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作。
在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
如何应对这种变化?如何提供一种 “封装机制”来隔离出“复杂对象的各个部分”的变化,从而保持系统中的“稳定构建算法”不随着需求改变而改变?
将一个复杂对象的构建与其表示相分离,使得同样的构建过
程(稳定)可以创建不同的表示(变化)。
——《设计模式》GoF
我们以房屋为例来展示建造者模式的应用。House
类代表房屋对象,而HouseBuilder
抽象类定义了构建房屋的步骤。StoneHouseBuilder
是HouseBuilder
的具体实现,用于构建石头房屋。HouseDirector
类负责指导具体建造者对象的构建过程。
通过使用建造者模式,我们可以将房屋对象的构建逻辑与客户端代码分离开来。客户端只需要创建一个合适的具体建造者对象,并将其传递给指导者进行构建。这样可以使客户端代码更简洁、可读性更高,并且在需要构建不同类型的房屋时更加灵活和扩展。
#include <iostream>
class House {
public:
void SetFoundation(const std::string& foundation) {
this->foundation = foundation;
}
void SetWalls(const std::string& walls) {
this->walls = walls;
}
void SetRoof(const std::string& roof) {
this->roof = roof;
}
void SetDoors(const std::string& doors) {
this->doors = doors;
}
void SetWindows(const std::string& windows) {
this->windows = windows;
}
void Show() {
std::cout << "房屋部分:" << std::endl;
std::cout << "地基:" << foundation << std::endl;
std::cout << "墙壁:" << walls << std::endl;
std::cout << "屋顶:" << roof << std::endl;
std::cout << "门:" << doors << std::endl;
std::cout << "窗户:" << windows << std::endl;
}
private:
std::string foundation; // 地基
std::string walls; // 墙壁
std::string roof; // 屋顶
std::string doors; // 门
std::string windows; // 窗户
};
class HouseBuilder {
public:
virtual ~HouseBuilder() {}
House* GetResult() {
return pHouse;
}
virtual void BuildFoundation() = 0; // 构建地基
virtual void BuildWalls() = 0; // 构建墙壁
virtual void BuildRoof() = 0; // 构建屋顶
virtual void BuildDoors() = 0; // 构建门
virtual void BuildWindows() = 0; // 构建窗户
protected:
House* pHouse;
};
class StoneHouseBuilder : public HouseBuilder {
public:
StoneHouseBuilder() {
pHouse = new House();
}
void BuildFoundation() override {
pHouse->SetFoundation("石头地基");
}
void BuildWalls() override {
pHouse->SetWalls("石头墙壁");
}
void BuildRoof() override {
pHouse->SetRoof("石头屋顶");
}
void BuildDoors() override {
pHouse->SetDoors("木门");
}
void BuildWindows() override {
pHouse->SetWindows("玻璃窗户");
}
};
class HouseDirector {
public:
HouseDirector(HouseBuilder* builder) {
pHouseBuilder = builder;
}
House* Construct() {
pHouseBuilder->BuildFoundation(); // 构建地基
pHouseBuilder->BuildWalls(); // 构建墙壁
pHouseBuilder->BuildRoof(); // 构建屋顶
pHouseBuilder->BuildDoors(); // 构建门
pHouseBuilder->BuildWindows(); // 构建窗户
return pHouseBuilder->GetResult();
}
private:
HouseBuilder* pHouseBuilder;
};
int main() {
HouseBuilder* builder = new StoneHouseBuilder(); // 使用石头房屋构建器
HouseDirector director(builder);
House* house = director.Construct(); // 构建房屋
house->Show(); // 展示房屋部分
delete house;
delete builder;
return 0;
}
输出:
房屋部分:
地基:石头地基
墙壁:石头墙壁
屋顶:石头屋顶
门:木门
窗户:玻璃窗户
这时候,假如我们有新需求,需要造一个黄金打造的房子,这时候就很方便了,只需要增加GoldenHouseBuilder
建造器,就可以完成这个扩展,不需要更改以前的代码,满足开放-封闭原则
class GoldenHouseBuilder : public HouseBuilder {
public:
GoldenHouseBuilder() {
pHouse = new House();
}
void BuildFoundation() override {
pHouse->SetFoundation("黄金地基");
}
void BuildWalls() override {
pHouse->SetWalls("黄金墙壁");
}
void BuildRoof() override {
pHouse->SetRoof("黄金屋顶");
}
void BuildDoors() override {
pHouse->SetDoors("金门");
}
void BuildWindows() override {
pHouse->SetWindows("钻石窗户");
}
};
调用过程
HouseBuilder* goldenBuilder = new GoldenHouseBuilder(); // 使用黄金房屋构建器
HouseDirector goldenDirector(goldenBuilder);
House* goldenHouse = goldenDirector.Construct(); // 构建黄金房屋
goldenHouse->Show(); // 展示黄金房屋部分
delete goldenHouse;
delete goldenBuilder;
房屋部分:
地基:黄金地基
墙壁:黄金墙壁
屋顶:黄金屋顶
门:金门
窗户:钻石窗户
在软件开发过程中,有时候需要构建具有复杂结构的对象。如果直接在客户端代码中创建和配置这些对象,会导致代码变得复杂且难以维护。此外,如果要构建多个具有不同配置的相似对象,每次都重复编写相似的构建代码也是低效的。为了解决这些问题,可以使用建造者模式。
建造者模式的核心思想是将对象的构建过程从其表示中分离出来。它允许你使用相同的构建过程来创建不同的表示形式。该模式将对象的构建委托给一个单独的建造者对象,该对象负责构建对象的各个部分,并最后返回构建好的对象。
在示例代码中,我们以房屋为例来展示建造者模式的应用。House
类代表房屋对象,而HouseBuilder
抽象类定义了构建房屋的步骤。StoneHouseBuilder
和GoldenHouseBuilder
是HouseBuilder
的具体实现,分别用于构建石头房屋和黄金房屋。HouseDirector
类负责指导具体建造者对象的构建过程。
通过使用建造者模式,我们可以将房屋对象的构建逻辑与客户端代码分离开来。客户端只需要创建一个合适的具体建造者对象,并将其传递给指导者进行构建。这样可以使客户端代码更简洁、可读性更高,并且在需要构建不同类型的房屋时更加灵活和扩展。
建造者模式适用于构建复杂对象的情况,将构建逻辑与表示分离,使得构建过程更加灵活和可扩展。它提供了一种组织和管理对象构建的方式,避免了繁琐和重复的构建代码。通过委托具体的建造者对象来构建对象,客户端代码可以专注于高级操作,而无需关心对象的内部构建细节。