建造者模式
- 建造者模式
- 组成部分
- 建造者模式使用步骤
- 1. 定义产品类
- 2. 创建具体产品类
- 3. 创建建造者接口
- 4. 实现具体建造者
- 5. 创建指挥者类
- 6. 客户端代码
- 建造者模式 UML 图
- 建造者模式 UML 图解析
- 建造者模式的优缺点
- 建造者模式的适用场景
- 完整代码
建造者模式
建造者模式(Builder Pattern)是一种创建型设计模式,它允许使用多个简单的对象一步步构建一个复杂的对象。建造者模式通过将对象的构建过程与表示分离,使得同样的构建过程可以创建不同的表示。
引入“建造者”模式的定义:将一个复杂的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
组成部分
- 产品(Product):代表最终构建的复杂对象,包含多个部件和属性。产品类定义了对象的结构和行为,通常包括多个属性和方法。
- 建造者(Builder):定义创建产品的各个部件的抽象接口。建造者接口提供了构建不同部分的方法,允许灵活地创建不同类型的产品。
- 具体建造者(ConcreteBuilder):实现建造者接口,具体构建和装配产品的各个部件。每个具体建造者负责创建特定类型的产品,同时维护构建过程的状态。
- 指挥者(Director):负责管理建造过程,调用建造者的方法以生成产品。指挥者定义构建的顺序,确保产品的构建过程符合预期。
建造者模式使用步骤
1. 定义产品类
首先定义一个复杂对象的类 Monster
,它是所有怪物的基类。
//怪物父类
class Monster
{
public:virtual ~Monster() {} //做父类时析构函数应该为虚函数// 其他方法可以在这里定义
};
2. 创建具体产品类
创建多个具体的怪物类,继承自 Monster
类。
//亡灵类怪物
class M_Undead :public Monster{};//元素类怪物
class M_Element :public Monster{};//机械类怪物
class M_Mechanic :public Monster{};
3. 创建建造者接口
定义一个抽象建造者类 MonsterBuilder
,声明构建产品各个部分的方法。
//怪物构建器父类
class MonsterBuilder
{
public:virtual ~MonsterBuilder() {} //做父类时析构函数应该为虚函数//返回指向Monster类的成员变量指针m_pMonster,当一个复杂的对象构建完成后,可以通过该成员函数把对象返回Monster* GetResult(){return m_pMonster;}virtual void LoadTrunkModel(string strno) = 0;//这里也可以写为空函数体,子类决定是否重新实现virtual void LoadHeadModel(string strno) = 0;virtual void LoadLimbsModel(string strno) = 0;protected:Monster* m_pMonster; //指向Monster类的成员变量指针
};
4. 实现具体建造者
创建多个具体建造者类,负责构建不同类型的怪物。
//亡灵类怪物构建器类
class M_UndeadBuilder :public MonsterBuilder
{
public:M_UndeadBuilder() //构造函数{m_pMonster = new M_Undead();}virtual void LoadTrunkModel(string strno){cout << "载入亡灵类怪物的躯干部位模型,需要m_pMonster指针调用M_Undead类或其父类中其他诸多成员函数,逻辑代码略......" << endl;//具体要做的事情其实是委托给怪物子类来完成,委托指把本该自己实现的功能转给其他类实现//m_pMonster->......略}virtual void LoadHeadModel(string strno){cout << "载入亡灵类怪物的头部模型并挂接到躯干部位,需要m_pMonster指针调用M_Undead类或其父类中其他诸多成员函数,逻辑代码略......" << endl;//m_pMonster->......略}virtual void LoadLimbsModel(string strno){cout << "载入亡灵类怪物的四肢模型并挂接到躯干部位,需要m_pMonster指针调用M_Undead类或其父类中其他诸多成员函数,逻辑代码略......" << endl;//m_pMonster->......略}
};//元素类怪物构建器类
class M_ElementBuilder :public MonsterBuilder
{
public:M_ElementBuilder() //构造函数{m_pMonster = new M_Element();}virtual void LoadTrunkModel(string strno){cout << "载入元素类怪物的躯干部位模型,需要m_pMonster指针调用M_Element类或其父类中其他诸多成员函数,逻辑代码略......" << endl;//m_pMonster->......略 }virtual void LoadHeadModel(string strno){cout << "载入元素类怪物的头部模型并挂接到躯干部位,需要m_pMonster指针调用M_Element类或其父类中其他诸多成员函数,逻辑代码略......" << endl;//m_pMonster->......略}virtual void LoadLimbsModel(string strno){cout << "载入元素类怪物的四肢模型并挂接到躯干部位,需要m_pMonster指针调用M_Element类或其父类中其他诸多成员函数,逻辑代码略......" << endl;//m_pMonster->......略}
};
//机械类怪物构建器类
class M_MechanicBuilder :public MonsterBuilder
{
public:M_MechanicBuilder() //构造函数{m_pMonster = new M_Mechanic();}virtual void LoadTrunkModel(string strno){cout << "载入机械类怪物的躯干部位模型,需要m_pMonster指针调用M_Mechanic类或其父类中其他诸多成员函数,逻辑代码略......" << endl;//m_pMonster->......略}virtual void LoadHeadModel(string strno){cout << "载入机械类怪物的头部模型并挂接到躯干部位,需要m_pMonster指针调用M_Mechanic类或其父类中其他诸多成员函数,逻辑代码略......" << endl;//m_pMonster->......略}virtual void LoadLimbsModel(string strno){cout << "载入机械类怪物的四肢模型并挂接到躯干部位,需要m_pMonster指针调用M_Mechanic类或其父类中其他诸多成员函数,逻辑代码略......" << endl;//m_pMonster->......略}
};
5. 创建指挥者类
定义一个指挥者类 MonsterDirector
,负责管理建造过程。
//指挥者类
class MonsterDirector
{
public:MonsterDirector(MonsterBuilder* ptmpBuilder) //构造函数{m_pMonsterBuilder = ptmpBuilder;}//指定新的构建器void SetBuilder(MonsterBuilder* ptmpBuilder){m_pMonsterBuilder = ptmpBuilder;}//原MonsterBuilder类中的Assemble成员函数Monster* Construct(string strmodelno) //参数:模型编号,形如“1253679201245”等,每些位的组合都有一些特别的含义,这里无需深究{m_pMonsterBuilder->LoadTrunkModel(strmodelno.substr(4, 3)); //载入躯干模型,截取某部分字符串以表示躯干模型的编号m_pMonsterBuilder->LoadHeadModel(strmodelno.substr(7, 3)); //载入头部模型并挂接到躯干模型上m_pMonsterBuilder->LoadLimbsModel(strmodelno.substr(10, 3)); //载入四肢模型并挂接到躯干模型上return m_pMonsterBuilder->GetResult(); //返回构建后的对象}
private:MonsterBuilder* m_pMonsterBuilder; //指向所有构建器类的父类
};
6. 客户端代码
在客户端中,创建具体建造者和指挥者的实例,使用指挥者构建产品。
int main()
{// 创建亡灵类怪物建造者M_UndeadBuilder* undeadBuilder = new M_UndeadBuilder();MonsterDirector undeadDirector(undeadBuilder); // 创建亡灵类怪物的指挥者// 构建亡灵类怪物Monster* undeadMonster = undeadDirector.Construct("1253679201245");// 使用构建的亡灵类怪物对象// ...// 创建元素类怪物的建造者M_ElementBuilder* elementBuilder = new M_ElementBuilder();MonsterDirector elementDirector(elementBuilder); // 创建元素类怪物的指挥者// 构建元素类怪物Monster* elementMonster = elementDirector.Construct("1253679201245");// 使用构建的元素类怪物对象// ...// 创建机械类怪物的建造者M_MechanicBuilder* mechanicBuilder = new M_MechanicBuilder();MonsterDirector mechanicDirector(mechanicBuilder); // 创建机械类怪物的指挥者// 构建机械类怪物Monster* mechanicMonster = mechanicDirector.Construct("1253679201245");// 使用构建的机械类怪物对象// ...// 清理资源delete undeadMonster; // 释放亡灵类怪物对象delete elementMonster; // 释放元素类怪物对象delete mechanicMonster; // 释放机械类怪物对象delete undeadBuilder; // 释放亡灵类建造者对象delete elementBuilder; // 释放元素类建造者对象delete mechanicBuilder; // 释放机械类建造者对象return 0;
}
建造者模式 UML 图
建造者模式 UML 图解析
- Builder (抽象构建器):
MonsterBuilder
类定义了创建产品对象的各个部件的抽象接口,包括方法如LoadTrunkModel
、LoadHeadModel
、LoadLimbsModel
。同时,它还定义了GetResult
接口,用于返回所创建的复杂对象。
- ConcreteBuilder (具体构建器):
- 具体构建器类(如
M_UndeadBuilder
、M_ElementBuilder
、M_MechanicBuilder
)实现了MonsterBuilder
接口,负责具体的构造和装配过程。每个具体构建器定义了其所创建的复杂对象(如M_Undead
、M_Element
、M_Mechanic
),并可能提供方法返回创建好的复杂对象。
- 具体构建器类(如
- Product (产品):
- 产品类指被构建的复杂对象,包含多个部件。具体构建器负责创建该产品的内部表示并定义它的装配过程。例如,
M_Undead
、M_Element
、M_Mechanic
类分别表示不同类型的怪物。
- 产品类指被构建的复杂对象,包含多个部件。具体构建器负责创建该产品的内部表示并定义它的装配过程。例如,
- Director (指挥者):
MonsterDirector
类是指挥者,持有一个指向抽象构建器的引用(如m_pMonsterBuilder
)。它利用该引用在Construct
成员函数中调用构建器对象的方法,完成复杂对象的构建。指挥者控制构建过程的顺序,确保各个部件按正确的顺序装配。例如,Construct
函数中会依次调用LoadTrunkModel
、LoadHeadModel
和LoadLimbsModel
。
- 客户端:
- 在客户端(如
main
函数)中,用户只需生成一个具体的构建器对象,并利用该构建器对象创建指挥者对象,调用指挥者的Construct
方法,就可以构建出所需的复杂对象。这种设计使得客户端与具体实现解耦,提高了灵活性和可维护性。
- 在客户端(如
建造者模式的优缺点
优点:
- 解耦:建造者模式将复杂对象的构建与表示分离,使得构建过程与产品的表示相互独立,降低了模块间的耦合度。
- 灵活性:可以通过更换具体建造者来灵活地构建不同类型的产品,而无需修改指挥者或客户端代码。
- 可读性和可维护性:通过清晰的接口和结构,增加了代码的可读性,便于后续的维护和扩展。
- 支持复杂对象的构建:适合构建复杂对象,特别是那些具有多个部件和装配顺序要求的对象。
- 步骤控制:指挥者可以控制构建的步骤和顺序,确保复杂对象按照预期的方式构建。
缺点:
- 复杂性:对于简单对象的构建,使用建造者模式可能显得过于复杂,增加了不必要的代码和结构。
- 类的数量增加:需要创建多个具体建造者类和产品类,可能导致类的数量增加,增加了系统的复杂性。
- 构建过程固定:一旦定义了指挥者的构建过程,可能不够灵活,难以适应一些动态变化的需求。
建造者模式的适用场景
- 构建复杂对象:当对象由多个部件组成,且构建和组合过程复杂时,例如游戏中的角色(如怪物、NPC)。
- 产品变体多样化:当需要创建多种不同类型的产品,但构建过程相似时,例如不同类型的车辆(轿车、卡车、摩托车)。
- 构建过程需要控制:当构建过程需要明确的步骤和顺序时,例如根据不同条件动态调整构建顺序。
- 对象的构建与表示分离:当产品的构建与表示需要解耦,以便于后续维护和扩展,例如构建复杂文档时。
- 逐步构建对象:当对象的构建过程需要分步进行,或在构建过程中进行条件判断时,例如配置复杂系统。
- 需要灵活的对象创建:当需要在运行时决定构建哪种类型的对象时,例如根据用户输入或配置文件动态选择不同的构建器。
完整代码
#include <iostream>
#include <string>using namespace std;//怪物父类
class Monster
{
public:virtual ~Monster() {} //做父类时析构函数应该为虚函数//void Assemble(string strmodelno) //参数:模型编号,形如“1253679201245”等,每些位的组合都有一些特别的含义,这里无需深究//{// LoadTrunkModel(strmodelno.substr(4, 3)); //载入躯干模型,截取某部分字符串以表示躯干模型的编号// LoadHeadModel(strmodelno.substr(7, 3)); //载入头部模型并挂接到躯干模型上// LoadLimbsModel(strmodelno.substr(10, 3)); //载入四肢模型并挂接到躯干模型上//}//virtual void LoadTrunkModel(string strno) = 0; //这里也可以写为空函数体,子类决定是否重新实现//virtual void LoadHeadModel(string strno) = 0;//virtual void LoadLimbsModel(string strno) = 0;
};//亡灵类怪物
class M_Undead :public Monster
{/*public:virtual void LoadTrunkModel(string strno){cout << "载入亡灵类怪物的躯干部位模型,需要调用M_Undead类或其父类中其他诸多成员函数,逻辑代码略......" << endl;}virtual void LoadHeadModel(string strno){cout << "载入亡灵类怪物的头部模型并挂接到躯干部位,需要调用M_Undead类或其父类中其他诸多成员函数,逻辑代码略......" << endl;}virtual void LoadLimbsModel(string strno){cout << "载入亡灵类怪物的四肢模型并挂接到躯干部位,需要调用M_Undead类或其父类中其他诸多成员函数,逻辑代码略......" << endl;}*/
};//元素类怪物
class M_Element :public Monster
{/*public:virtual void LoadTrunkModel(string strno){cout << "载入元素类怪物的躯干部位模型,需要调用M_Element类或其父类中其他诸多成员函数,逻辑代码略......" << endl;}virtual void LoadHeadModel(string strno){cout << "载入元素类怪物的头部模型并挂接到躯干部位,需要调用M_Element类或其父类中其他诸多成员函数,逻辑代码略......" << endl;}virtual void LoadLimbsModel(string strno){cout << "载入元素类怪物的四肢模型并挂接到躯干部位,需要调用M_Element类或其父类中其他诸多成员函数,逻辑代码略......" << endl;}*/
};//机械类怪物
class M_Mechanic :public Monster
{/*public:virtual void LoadTrunkModel(string strno){cout << "载入机械类怪物的躯干部位模型,需要调用M_Mechanic类或其父类中其他诸多成员函数,逻辑代码略......" << endl;}virtual void LoadHeadModel(string strno){cout << "载入机械类怪物的头部模型并挂接到躯干部位,需要调用M_Mechanic类或其父类中其他诸多成员函数,逻辑代码略......" << endl;}virtual void LoadLimbsModel(string strno){cout << "载入机械类怪物的四肢模型并挂接到躯干部位,需要调用M_Mechanic类或其父类中其他诸多成员函数,逻辑代码略......" << endl;}*/
};//-----------------
//怪物构建器父类
class MonsterBuilder
{
public:virtual ~MonsterBuilder() {} //做父类时析构函数应该为虚函数//void Assemble(string strmodelno) //参数:模型编号,形如“1253679201245”等,每些位的组合都有一些特别的含义,这里无需深究//{// LoadTrunkModel(strmodelno.substr(4, 3)); //载入躯干模型,截取某部分字符串以表示躯干模型的编号// LoadHeadModel(strmodelno.substr(7, 3)); //载入头部模型并挂接到躯干模型上// LoadLimbsModel(strmodelno.substr(10, 3)); //载入四肢模型并挂接到躯干模型上//}//返回指向Monster类的成员变量指针m_pMonster,当一个复杂的对象构建完成后,可以通过该成员函数把对象返回Monster* GetResult(){return m_pMonster;}virtual void LoadTrunkModel(string strno) = 0;//这里也可以写为空函数体,子类决定是否重新实现virtual void LoadHeadModel(string strno) = 0;virtual void LoadLimbsModel(string strno) = 0;protected:Monster* m_pMonster; //指向Monster类的成员变量指针
};//---------------
//亡灵类怪物构建器类
class M_UndeadBuilder :public MonsterBuilder
{
public:M_UndeadBuilder() //构造函数{m_pMonster = new M_Undead();}virtual void LoadTrunkModel(string strno){cout << "载入亡灵类怪物的躯干部位模型,需要m_pMonster指针调用M_Undead类或其父类中其他诸多成员函数,逻辑代码略......" << endl;//具体要做的事情其实是委托给怪物子类来完成,委托指把本该自己实现的功能转给其他类实现//m_pMonster->......略}virtual void LoadHeadModel(string strno){cout << "载入亡灵类怪物的头部模型并挂接到躯干部位,需要m_pMonster指针调用M_Undead类或其父类中其他诸多成员函数,逻辑代码略......" << endl;//m_pMonster->......略}virtual void LoadLimbsModel(string strno){cout << "载入亡灵类怪物的四肢模型并挂接到躯干部位,需要m_pMonster指针调用M_Undead类或其父类中其他诸多成员函数,逻辑代码略......" << endl;//m_pMonster->......略}
};//元素类怪物构建器类
class M_ElementBuilder :public MonsterBuilder
{
public:M_ElementBuilder() //构造函数{m_pMonster = new M_Element();}virtual void LoadTrunkModel(string strno){cout << "载入元素类怪物的躯干部位模型,需要m_pMonster指针调用M_Element类或其父类中其他诸多成员函数,逻辑代码略......" << endl;//m_pMonster->......略 }virtual void LoadHeadModel(string strno){cout << "载入元素类怪物的头部模型并挂接到躯干部位,需要m_pMonster指针调用M_Element类或其父类中其他诸多成员函数,逻辑代码略......" << endl;//m_pMonster->......略}virtual void LoadLimbsModel(string strno){cout << "载入元素类怪物的四肢模型并挂接到躯干部位,需要m_pMonster指针调用M_Element类或其父类中其他诸多成员函数,逻辑代码略......" << endl;//m_pMonster->......略}
};
//机械类怪物构建器类
class M_MechanicBuilder :public MonsterBuilder
{
public:M_MechanicBuilder() //构造函数{m_pMonster = new M_Mechanic();}virtual void LoadTrunkModel(string strno){cout << "载入机械类怪物的躯干部位模型,需要m_pMonster指针调用M_Mechanic类或其父类中其他诸多成员函数,逻辑代码略......" << endl;//m_pMonster->......略}virtual void LoadHeadModel(string strno){cout << "载入机械类怪物的头部模型并挂接到躯干部位,需要m_pMonster指针调用M_Mechanic类或其父类中其他诸多成员函数,逻辑代码略......" << endl;//m_pMonster->......略}virtual void LoadLimbsModel(string strno){cout << "载入机械类怪物的四肢模型并挂接到躯干部位,需要m_pMonster指针调用M_Mechanic类或其父类中其他诸多成员函数,逻辑代码略......" << endl;//m_pMonster->......略}
};//指挥者类
class MonsterDirector
{
public:MonsterDirector(MonsterBuilder* ptmpBuilder) //构造函数{m_pMonsterBuilder = ptmpBuilder;}//指定新的构建器void SetBuilder(MonsterBuilder* ptmpBuilder){m_pMonsterBuilder = ptmpBuilder;}//原MonsterBuilder类中的Assemble成员函数Monster* Construct(string strmodelno) //参数:模型编号,形如“1253679201245”等,每些位的组合都有一些特别的含义,这里无需深究{m_pMonsterBuilder->LoadTrunkModel(strmodelno.substr(4, 3)); //载入躯干模型,截取某部分字符串以表示躯干模型的编号m_pMonsterBuilder->LoadHeadModel(strmodelno.substr(7, 3)); //载入头部模型并挂接到躯干模型上m_pMonsterBuilder->LoadLimbsModel(strmodelno.substr(10, 3)); //载入四肢模型并挂接到躯干模型上return m_pMonsterBuilder->GetResult(); //返回构建后的对象}
private:MonsterBuilder* m_pMonsterBuilder; //指向所有构建器类的父类
};int main()
{// 创建具体建造者M_UndeadBuilder* undeadBuilder = new M_UndeadBuilder();MonsterDirector undeadDirector(undeadBuilder); // 创建亡灵类怪物的指挥者// 构建亡灵类怪物Monster* undeadMonster = undeadDirector.Construct("1253679201245");// 使用构建的亡灵类怪物对象// ...// 创建元素类怪物的建造者M_ElementBuilder* elementBuilder = new M_ElementBuilder();MonsterDirector elementDirector(elementBuilder); // 创建元素类怪物的指挥者// 构建元素类怪物Monster* elementMonster = elementDirector.Construct("1253679201245");// 使用构建的元素类怪物对象// ...// 创建机械类怪物的建造者M_MechanicBuilder* mechanicBuilder = new M_MechanicBuilder();MonsterDirector mechanicDirector(mechanicBuilder); // 创建机械类怪物的指挥者// 构建机械类怪物Monster* mechanicMonster = mechanicDirector.Construct("1253679201245");// 使用构建的机械类怪物对象// ...// 清理资源delete undeadMonster; // 释放亡灵类怪物对象delete elementMonster; // 释放元素类怪物对象delete mechanicMonster; // 释放机械类怪物对象delete undeadBuilder; // 释放亡灵类建造者对象delete elementBuilder; // 释放元素类建造者对象delete mechanicBuilder; // 释放机械类建造者对象return 0;
}