【计划模式】制作者模式

[复制链接]
发表于 2025-10-12 07:41:26 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
三、制作者模式

3.3 制作者模式

制作者(Builder)  模式也称构建器模式、构建者模式或天生器模式,同工厂模式或原型 模式一样,也是一种创建型模式。制作者模式比力复杂,不太常用,但这并不表现不必要了 解和把握该模式。制作者模式通常用来创建一个比力复杂的对象(这也是制作者模式本身 比力复杂的紧张缘故起因),该对象的构建一样平常是必要按肯定序次分步调举行的。比方,制作一 座房子(无论是平房、别墅照旧高楼),通常都必要按序次制作地基、修建体、修建顶等步调, 制作一辆汽车通常会包罗发动机、方向盘、轮胎等部件,创建一份报表也通常会包罗表头、表  身、表尾等部门。
3.3.1  一个具体实现范例的徐徐重构

这里照旧以游戏中的怪物类来解说。怪物同样分为亡灵类怪物、元素类怪物、机器类怪 物 。
在创建怪物对象的过程中,有一个创建步调非常烦琐——把怪物模子创建出来用于显 示给玩家。策划规定,任何一种怪物都由头 部、躯干(包罗颈部、尾巴等)、肢体 3个部位组 成,在制作怪物模子时,头部、躯干、肢体模子分开制作。每个部位模子都会有一些位置和方 向信息,用于挂接在其他部位模子上,比如将头部挂接到躯干部,再将肢体挂接到躯干部就 可以构成一个完备的怪物模子。固然, 一些在水中的怪物大概不包罗四肢,那么将肢体挂接 到躯干部这个步调什么都不做即可。
之以是在制作怪物模子时将头部、躯干、肢体模子分开制作,是便于同范例怪物的3个 构成部位举行互换。试想一下,假如针对亡灵类怪物制作了3个头部、3个躯干以及3个肢 体,则最多可以组合出27个外貌差别的亡灵类怪物(固然,有些组合看起来会比力貌寝,不 适适用在游戏中),这既节省了游戏制作本钱,又节省了游戏运行时对内存的斲丧。
步调必要先把怪物模子载入内存并举行装配以包管精确地体现给玩家看。以是步调需 要举行如下编码步调:

  • (1)将怪物的躯干模子信息读入内存并提取此中的位置和方向信息;
  • (2)将怪物的头部和四肢模子信息读入内存并提取此中的位置和方向信息;
  • (3)将头部和四肢模子以精确的位置和方向挂接(Mount)   到躯干部位,从而装配出完 整的怪物模子。
    由于解说的偏重点差别,以是在这里重新实现 Monster   怪物类,在该类中引入 Assemble  成员函数,用于装配一个怪物,代码大概如下:
  1. class Monster
  2. {
  3. public:
  4.         virtual ~Monster();
  5.        
  6.         void Assemble(std::string strmodelno);
  7.         virtual void LoadTrunckModel(std::string strno) = 0;
  8.         virtual void LoadHeadModel(std::string strno) = 0;
  9.         virtual void LoadLimbsModel(std::string strno) = 0;
  10. };
  11. Monster::~Monster()
  12. {
  13. }
  14. void Monster::Assemble(std::string strmodelno) { //9位数
  15.         LoadTrunckModel(strmodelno.substr(0, 3));
  16.         LoadHeadModel(strmodelno.substr(3, 3));
  17.         LoadLimbsModel(strmodelno.substr(6, 3));
  18. }
复制代码
上述代码只是大抵的实当代码,在Assemble  成员函数中实现了载入一个怪物模子的 固定流程——分别载入了躯干、头部、四肢模子并将它们装配到一起,游戏中全部怪物的载 入都依照该流程(此中的代码是稳固的,不发生变革),以是这里的 Assemble  成员函数很像 模板方法模式中的模板方法。
笔者在上述代码中做了很多简化,比方 LoadTrunkModel    载入躯干模子时大概要返回 一个与载入效果干系的结构(模子结构)以转达到后续即将调用的 LoadHeadModel  和 LoadLimbsModel   成员函数中,如许这两个成员函数就可以在载入头部和四肢模子时完满 (继承添补)该结构等,由于这些内容与计划模式无关,以是全部省略。
由于亡灵类怪物、元素类怪物、机器类怪物的外貌差异巨大,以是固然这3类怪物的载入 流程雷同,但差别种类怪物的细节载入差异很大,以是,将LoadTrunkModel、LoadHeadModel、
LoadLimbsModel  (构建模子的子步调)成员函数写为虚函数以方便在Monster  的子类中重 新实现。
有些读者大概会渴望将 Assemble  成员函数的内容放到 Monster  类构造函数中以到达 怪物对象创建时就载入模子数据的目标,但在本书附录A 中将重点夸大,不要在类的构造 函数与析构函数中调用虚函数以防止出现标题,而Assemble 调用的都是虚函数,以是,切 不 可 将 Assemble    成员函数的内容放到 Monster  类构造函数中实现。
接下来分别实现继承自父类Monster  的亡灵类怪物、元素类怪物、机器类怪物干系类 M_Undead、M_Element、M_Mechanic,  代码如下:
  1. class M_Undead : public Monster {
  2. public:
  3.         virtual void LoadTrunckModel(std::string strno) override;
  4.         virtual void LoadHeadModel(std::string strno) override;
  5.         virtual void LoadLimbsModel(std::string strno) override;
  6. };
  7. class M_Element : public Monster {
  8. public:
  9.         virtual void LoadTrunckModel(std::string strno) override;
  10.         virtual void LoadHeadModel(std::string strno) override;
  11.         virtual void LoadLimbsModel(std::string strno) override;
  12. };
  13. class M_Mechanic : public Monster {
  14. public:
  15.         virtual void LoadTrunckModel(std::string strno) override;
  16.         virtual void LoadHeadModel(std::string strno) override;
  17.         virtual void LoadLimbsModel(std::string strno) override;
  18. };
  19. void M_Mechanic::LoadTrunckModel(std::string strno) {
  20.         std::cout << "载入机械类怪物的躯干" << std::endl;
  21. }
  22. void M_Mechanic::LoadHeadModel(std::string strno) {
  23.         std::cout << "载入机械类怪物的头部" << std::endl;
  24. }
  25. void M_Mechanic::LoadLimbsModel(std::string strno) {
  26.         std::cout << "载入机械类怪物的四肢" << std::endl;
  27. }
  28. void M_Element::LoadTrunckModel(std::string strno) {
  29.         std::cout << "载入元素类怪物的躯干" << std::endl;
  30. }
  31. void M_Element::LoadHeadModel(std::string strno) {
  32.         std::cout << "载入元素类怪物的头部" << std::endl;
  33. }
  34. void M_Element::LoadLimbsModel(std::string strno) {
  35.         std::cout << "载入元素类怪物的四肢" << std::endl;
  36. }
  37. void M_Undead::LoadTrunckModel(std::string strno) {
  38.         std::cout << "载入亡灵类怪物的躯干" << std::endl;
  39. }
  40. void M_Undead::LoadHeadModel(std::string strno) {
  41.         std::cout << "载入亡灵类怪物的头部" << std::endl;
  42. }
  43. void M_Undead::LoadLimbsModel(std::string strno) {
  44.         std::cout << "载入亡灵类怪物的四肢" << std::endl;
  45. }
复制代码
可以看到,在代码中,创建了一只元素类怪物对象,然后调用Assemble  成员函数对怪 物模子举行装配以用于后续的怪物体现。
上述代码看起来更像是模板方法模式。但阅读代码应该更偏重代码的实现目标而非代码的实现结构,这些代码用于创建怪物对象以体现给玩家看,但怪物的创建比力复杂,严格 地说,应该是怪物模子的载入过程比力复杂,必要按序次分别载入躯干、头部、四肢模子并实 现差别部位模子之间的挂接。至此,可以说所需的功能(指模子载入功能)已经完成,假如程 序员不再继承开发,也是可以的。但是,现在的代码实现结构还不能称为制作者模式,通过 对步调进一步拆分还可以进一步提拔机动性。
这里将 Assemble 、LoadTrunkModel 、LoadHeadModel 、LoadLimbsModel    这些与模子 载入与挂接步调干系的成员函数称为构建过程干系函数。思量到Monster  类中要实现的 逻辑功能大概较多,假如把构建过程干系函数提取出来(分离)放到一个单独的类中,不但可 以镌汰Monster  类中的代码量,还可以增长构建过程干系代码的独立性,日后游戏中任何 由头部、躯干、肢体 3个部位构成并必要将头部挂接到躯干部,再将肢体挂接到躯干部的生 物,都可以通过这个单独的类实现模子的构建。
引入与怪物类同条理的干系构建器类,把怪物类中的代码搬到干系的构建器类中,代码 如 下 :
  1. class MonsterBuilder
  2. {
  3. public:
  4.         virtual ~MonsterBuilder();
  5.         void Assemble(std::string strmodelno);
  6.         virtual void LoadTrunckModel(std::string strno) = 0;
  7.         virtual void LoadHeadModel(std::string strno) = 0;
  8.         virtual void LoadLimbsModel(std::string strno) = 0;
  9.         Monster* GetResult();
  10. protected:
  11.         Monster* m_pMonster = nullptr;
  12. };
  13. class M_Undead;
  14. class M_UndeadBuilder : public MonsterBuilder
  15. {
  16. public:
  17.         M_UndeadBuilder();
  18.         virtual ~M_UndeadBuilder();
  19.         virtual void LoadTrunckModel(std::string strno) override;
  20.         virtual void LoadHeadModel(std::string strno) override;
  21.         virtual void LoadLimbsModel(std::string strno) override;
  22. };
  23. class M_ElementBuilder:MonsterBuilder
  24. {
  25. public:
  26.         virtual ~M_ElementBuilder();
  27.        
  28.         virtual void LoadTrunckModel(std::string strno) = 0;
  29.         virtual void LoadHeadModel(std::string strno) = 0;
  30.         virtual void LoadLimbsModel(std::string strno) = 0;
  31. };
  32. class M_MechanicBuilder :MonsterBuilder
  33. {
  34. public:
  35.         virtual ~M_MechanicBuilder();
  36.         virtual void LoadTrunckModel(std::string strno) = 0;
  37.         virtual void LoadHeadModel(std::string strno) = 0;
  38.         virtual void LoadLimbsModel(std::string strno) = 0;
  39. };
复制代码
在上述代码中,可以注意到,在MonsterBuilder  类中放置了一个指向 Monster  类的成  员变量指针 m_pMonster,     同时引入 GetResult     成员函数用于返回这个m  pMonster      指 针 , 也就是说,当一个复杂的对象通过构建器构建完成后,可以通过GetResult   返回。分别为  Monster  的子类M_Undead 、M _Element 、M_Mechanic 创建对应的父类为 MonsterBuilder    的构建子类M_UndeadBuilder 、M_ElementBuilder 、M_MechanicBuilder,    由于工厂方法模 式是创建一个与产物品级结构雷同的工厂品级结构,以是这部门看起来似乎与工厂方法模 式有些相似之处。
重点观察 MonsterBuilder  类中的Assemble  成员函数,前面曾经提过,该成员函数中的 代码是稳固的,不会发生变革。以是可以继承把Assemble  成员函数的功能拆出到一个新 类中(这步拆分也不是必须的)。创建新类 MonsterDirector  (饰演一个指挥者脚色),将 MonsterBuilder  类中的 Assemble  成员函数整个迁徙到 MonsterDirector   类中并按照惯例 重新定名为Construct,   同时,在 MonsterDirector   类中放置一个指向 MonsterBuilder   类的 成员变量指针m_pMonsterBuilder,    同时对Construct   成员函数的代码举行调解(注意也增 加了返回值)。完备的MonsterDirector  类代码如下:
  1. class MonsterDirector
  2. {
  3. public:
  4.         MonsterDirector(MonsterBuilder* ptmpBuilder);
  5.         void SetBuilder(MonsterBuilder* ptmpBuilder);
  6.         Monster* Construct(std::string strmodelno);
  7. private:
  8.         MonsterBuilder* m_pMonsterBuilder = nullptr;
  9. };
复制代码
3.3.2  引入制作者模式

从前面的代码可以看到,制作者模式的实当代码相对比力复杂。
引入“制作者”模式的界说:将一个复杂对象的构建与它的表现分离,使得同样的构建 过程可以创建差别的表现。
在上述范例中,MonsterBuilder   类是对象的构建,而Monster  类是对象的表现,这两个 类是相互分离的。构建过程是指MonsterDirector   类中的 Construct   成员函数所代表的怪 物模子的载入和装配(挂接)过程,该过程稳固不会发生变革(稳固的算法),以是只要转达给 MonsterDirector   差别的构建器子类(M_UndeadBuilder 、M_ElementBuilder 、M_MechanicBuilder),就会构建出差别的怪物,可以随时调用 MonsterDirector    类 的 SetBuilder   成员函数为 MonsterDirector(指挥者)指定一个新的构建器以创建差别种类的怪物对象。
针对前面的范例绘制制作者模式的UML  图,如图3.8所示。
在图3 .8中,重点观看除抽象产物父类和具体产物类(Monster 、M_Undead 、M_Element、M_Mechanic) 之外的其他类。图3.8中的空心菱形在哪个类这边,就表现哪个类 中包罗别的一个类的对象指针(这里表现 MonsterDirector   类中包罗指向MonsterBuilder   类对象的指针m_pMonsterBuilder)   作为成员变量。
制作者模式的 UML 图包罗4种脚色。

  • (1)Builder     (抽象构建器):为创建 一 个产物对象的各个部件指定抽象接口 (LoadTrunkModel、LoadHeadModel、LoadLimbsModel), 同时,也会指定一个接口(GetResult) 用于返回所创建的复杂对象。这里指MonsterBuilder类。
  • 2)ConcreteBuilder(具体构建器):实现了Builder接口以创建(构造和装配)该产物的 各个部件,界说并明确其所创建的复杂对象,偶然也可以提供一个方法用于返回创建好的复 杂对象。这里指M_UndeadBuilder 、M_ElementBuilder 、M_MechanicBuilder   类。
  • (3)Product    (产物):指的是被构建的复杂对象,其包罗多个部件,由具体构建器创建该 产物的内部表现并界说它的装配过程。这里指M_Undead 、M_Element 、M_Mechanic 类 。
  • (4)Director    (指挥者):又称导演类,这里指 MonsterDirector   类。该类有一个指向抽 象构建器的指针(m_pMonsterBuilder),     利用该指针可以在Construct   成员函数中调用构建 器对象中“构建和装配产物部件”的方法来完成复杂对象的构建,只要指定差别的具体构建 器,用雷同的构建过程就会构建出差别的产物。同时,Construct 成员函数还控制复杂对象 的构建序次(比方,在 Construct 成 员 函 数 中 对 LoadTrunkModel、LoadHeadModel、
LoadLimbsModel 的调用是有先后序次的)。在客户端(指main 主函数中的调用代码)只需 要天生一个具体的构建器对象,并利用该构建器对象创建指挥者对象并调用指挥者类的 Construct  成员函数,就可以构建一个复杂的对象。
前面已经说过,从MonsterBuilder   分拆出MonsterDirector    这步不是必须的,不做分拆 可以看作制作者模式的一种退化情况,固然,此时客户端就必要直接针对构建器举行编码 了。一样平常的发起是:假如MonsterBuilder类本身非常巨大、非常复杂,则举行分拆,否则可 以不举行分拆,总之——复杂的东西就思量做拆解,简朴的东西就思量做归并。
3.3.3  另一个制作者模式的范例

为了进一步加深读者对制作者模式的明白,再来报告一个比力常见的应用制作者模式 的 范 例 。
某公司各部门的员工工作日报中包罗标题、内容主体、末端3部门。 ·标题部门包罗部门名称、日报天生日期等信息。

  • 内容主体部门就是具体的形貌数据(包罗该项工作内容形貌和完成该项工作泯灭的 时间),具体形貌数据大概会有多条(该员工一天大概做了多项工作)。
  • 末端部门包罗日报所属员工姓名。
    现在要将工作日报导出成多种格式的文件,比方导出成纯文本格式、XML 格式、JSON  格式等,工作日报中内容主体部门的形貌数据大概会有多条,导出到文件时每条数据占用 一 行 。

  • 不消计划模式时步调应该怎样誊写
    针对上面的需求,看一看不接纳计划模式时应该怎样编写步调代码。可以把工作日报 中所包罗的3部门内容分别界说3个类来实现,起首界说一个类来表达日报中的标题部门:
  1. class DailyHeaderData
  2. {
  3. public:
  4.         DailyHeaderData(std::string strDepName, std::string strGenDate);
  5.         std::string getDepName();
  6.         std::string getExportDate();
  7. private:
  8.         std::string m_strDepName;
  9.         std::string m_strGenDate;
  10. };
  11. class DailyContentData {
  12. public:
  13.         DailyContentData(std::string strContent, double dspendTime);
  14.         std::string getContent();
  15.         double getSpendTime();
  16. private:
  17.         std::string m_strContent;
  18.         double m_dspendTime;
  19. };
  20. class DailyFooterData {
  21. public:
  22.         DailyFooterData(std::string strUserName);
  23.         std::string getUserName();
  24. private:
  25.         std::string m_strUserName;
  26. };
  27. //将日报导出到纯文本格式文件 相关的类
  28. class ExportToTxtFile
  29. {
  30. public:
  31.         //实现导出动作
  32.         void doExport(DailyHeaderData& dailyheaderobj, std::vector<DailyContentData*>& vec_dailycontobj, DailyFooterData& dailyfooterobj);
  33. };
  34. DailyHeaderData::DailyHeaderData(std::string strDepName, std::string strGenDate)
  35.     :m_strDepName(strDepName),m_strGenDate(strGenDate)
  36. {
  37. }
  38. std::string DailyHeaderData::getDepName()
  39. {
  40.     return m_strDepName;
  41. }
  42. std::string DailyHeaderData::getExportDate()
  43. {
  44.     return m_strGenDate;
  45. }
  46. DailyContentData::DailyContentData(std::string strContent, double dspendTime)
  47.     :m_strContent(strContent), m_dspendTime(dspendTime)
  48. {   
  49. }
  50. std::string DailyContentData::getContent() {
  51.     return m_strContent;
  52. }
  53. double DailyContentData::getSpendTime() {
  54.     return m_dspendTime;
  55. }
  56. DailyFooterData::DailyFooterData(std::string strUserName)
  57.     :m_strUserName(strUserName)
  58. {}
  59. std::string DailyFooterData::getUserName() {
  60.     return m_strUserName;
  61. }
  62. //实现导出动作
  63. void ExportToTxtFile::doExport(DailyHeaderData& dailyheaderobj, std::vector<DailyContentData*>& vec_dailycontobj, DailyFooterData& dailyfooterobj) //记得#include头文件vector,因为日报的内容主体部分中的描述数据可能会有多条,所以用vector容器保存
  64. {
  65.         std::string strtmp = "";
  66.         //(1)拼接标题
  67.         strtmp += dailyheaderobj.getDepName() + "," + dailyheaderobj.getExportDate() + "\n";
  68.         //(2)拼接内容主体,内容主体中的描述数据会有多条,因此需要迭代
  69.         for (auto iter = vec_dailycontobj.begin(); iter != vec_dailycontobj.end(); ++iter)
  70.         {
  71.                 std::stringstream oss; //记得#include头文件sstream
  72.                 oss << (*iter)->getSpendTime();
  73.                 strtmp += (*iter)->getContent() + ":(花费的时间:" + oss.str() + "小时)" + "\n";
  74.         } //end for
  75.           //(3)拼接结尾
  76.         strtmp += "报告人:" + dailyfooterobj.getUserName() + "\n";
  77.         //(4)导出到真实文件的代码略,只展示在屏幕上文件的内容
  78.         std::cout << strtmp;
  79. }
复制代码
在 main 主函数中到场代码,来展示一下导出到纯文本格式文件中的内容:
  1.         DailyHeaderData* pdhd = new DailyHeaderData("研发一部", "11月1日");
  2.         DailyContentData* pdcd1 = new DailyContentData("完成A项目的需求分析工作", 3.5);
  3.         DailyContentData* pdcd2 = new DailyContentData("确定A项目开发所使用的工具", 4.5);
  4.        
  5.         std::vector<DailyContentData*> vec_dcd;
  6.         vec_dcd.push_back(pdcd1);
  7.         vec_dcd.push_back(pdcd2);
  8.         DailyFooterData* pdfd = new DailyFooterData("小李");
  9.         ExportToTxtFile file_ettxt;
  10.         file_ettxt.doExport(*pdhd, vec_dcd, *pdfd);
  11.         for (auto iter = vec_dcd.begin(); iter != vec_dcd.end(); ++iter) {
  12.                 delete (*iter);
  13.         }
  14.         delete pdfd;
  15.         delete pdhd;
复制代码
如 果 想 将 员 工 工 作 日 报 数 据 导 出 到 XML      格 式 的 文 件 中 , 可 以 编 写 另 一 个 类
ExportToXmlFile, 代码如下:
  1. //将日报导出到XML格式文件 相关的类
  2. class ExportToXmlFile
  3. {
  4. public:
  5.         //实现导出动作
  6.         void doExport(DailyHeaderData& dailyheaderobj, std::vector<DailyContentData*>& vec_dailycontobj, DailyFooterData& dailyfooterobj);
  7. };
  8. //实现导出动作
  9. void ExportToXmlFile::doExport(DailyHeaderData& dailyheaderobj, std::vector<DailyContentData*>& vec_dailycontobj, DailyFooterData& dailyfooterobj) //记得#include头文件std::vector,因为日报的内容主体部分中的描述数据可能会有多条,所以用std::vector容器保存
  10. {
  11.         std::string strtmp = "";
  12.         //(1)拼接标题
  13.         strtmp += "<?xml version="1.0" encoding="UTF-8" ?>\n";
  14.         strtmp += "<DailyReport>\n";
  15.         strtmp += "    <Header>\n";
  16.         strtmp += "        <DepName>" + dailyheaderobj.getDepName() + "</DepName>\n";
  17.         strtmp += "        <GenDate>" + dailyheaderobj.getExportDate() + "</GenDate>\n";
  18.         strtmp += "    </Header>\n";
  19.         //(2)拼接内容主体,内容主体中的描述数据会有多条,因此需要迭代
  20.         strtmp += "    <Body>\n";
  21.         for (auto iter = vec_dailycontobj.begin(); iter != vec_dailycontobj.end(); ++iter)
  22.         {
  23.                 std::stringstream oss; //记得#include头文件sstream
  24.                 oss << (*iter)->getSpendTime();
  25.                 strtmp += "        <Content>" + (*iter)->getContent() + "</Content>\n";
  26.                 strtmp += "        <SpendTime>花费的时间:" + oss.str() + "小时" + "</SpendTime>\n";
  27.         } //end for
  28.         strtmp += "    </Body>\n";
  29.         //(3)拼接结尾
  30.         strtmp += "    <Footer>\n";
  31.         strtmp += "        <UserName>报告人:" + dailyfooterobj.getUserName() + "</UserName>\n";
  32.         strtmp += "    </Footer>\n";
  33.         strtmp += "</DailyReport>\n";
  34.         //(4)导出到真实文件的代码略,只展示在屏幕上文件的内容
  35.         std::cout << strtmp;
  36. }
复制代码
从上述范例中可以看到,无论是将工作日报导出到纯文本格式文件中照旧导出到XML 格式文件中,如下3个步调始终是稳固不会发生变革的:

  • 拼接标题;
  • 拼接内容主体;
  • 拼接末端。
固然导出到的文件格式差别,上述3个步调每一步的具体实当代码差别,但对于差别格 式的文件,这3个步调是重复的,以是思量把这3个步调(复杂对象的构建过程)提炼(抽象) 出来,形成一个通用的处置处罚过程,如许以后只要给这个处置处罚过程转达差别的参数,就可以控 制该过程导出差别格式的文件。这也就是制作者模式的初志——将构建差别格式数据的细节实当代码与具体的构建步调分离,以到达复用构建步调的目标。

  • 接纳计划模式时步调应该怎样改写
    可以参考前面接纳制作者计划模式的范例来誊写本范例。先实现抽象构建器 FileBuilder 类(文件构建器父类),用于为上述3个步调指定抽象接口,代码如下:
  1. class FileBuilder{
  2. public:
  3.         virtual ~FileBuilder();
  4. public:
  5.         virtual void buildHeader(DailyHeaderData& dailyheaderobj) = 0;
  6.         virtual void buildBody(std::vector<DailyContentData*>& vec_dailycontobj) = 0;
  7.         virtual void buildFooter(DailyFooterData& dailyfooterobj) = 0;
  8.        
  9.         std::string GetResult();
  10. protected:
  11.         std::string m_strResult;
  12. };
  13. FileBuilder::~FileBuilder()
  14. {
  15. }
  16. std::string FileBuilder::GetResult()
  17. {
  18.         return m_strResult;
  19. }
复制代码
紧接着,构建两个FileBuilder  的子类——纯文本文件构建器类TxtFileBuilder   和 XML   文件构建器类XmlFileBuilder,  以实现FileBuilder  类中界说的接口。 TxtFileBuilder   中接口 的实当代码与前述 ExportToTxtFile    类 中 doExport   成员函数的实当代码非常类似, XmlFileBuilder  中接口的实当代码与前述 ExportToXmlFile   类 中doExport   成员函数的实  当代码非常类似。
  1. class TextFileBuilder : public FileBuilder {
  2. public:
  3.         virtual ~TextFileBuilder();
  4.         virtual void buildHeader(DailyHeaderData& dailyheaderobj) override;
  5.         virtual void buildBody(std::vector<DailyContentData*>& vec_dailycontobj) override;
  6.         virtual void buildFooter(DailyFooterData& dailyfooterobj) override;
  7. };
  8. class XmlFileBuilder : public FileBuilder {
  9. public:
  10.         virtual ~XmlFileBuilder();
  11.         virtual void buildHeader(DailyHeaderData& dailyheaderobj) override;
  12.         virtual void buildBody(std::vector<DailyContentData*>& vec_dailycontobj) override;
  13.         virtual void buildFooter(DailyFooterData& dailyfooterobj) override;
  14. };
  15. TextFileBuilder::~TextFileBuilder()
  16. {
  17. }
  18. void TextFileBuilder::buildHeader(DailyHeaderData& dailyheaderobj)
  19. {
  20.         m_strResult += dailyheaderobj.getDepName() + "," + dailyheaderobj.getExportDate() + "\n";
  21. }
  22. void TextFileBuilder::buildBody(std::vector<DailyContentData*>& vec_dailycontobj)
  23. {
  24.         for (auto iter = vec_dailycontobj.begin(); iter != vec_dailycontobj.end(); ++iter)
  25.         {
  26.                 std::ostringstream oss;
  27.                 oss << (*iter)->getSpendTime();
  28.                 m_strResult += (*iter)->getContent() + ":(花费的时间:" + oss.str() + "小时)" + "\n";
  29.         }
  30. }
  31. void TextFileBuilder::buildFooter(DailyFooterData& dailyfooterobj)
  32. {
  33.         m_strResult += "报告人:" + dailyfooterobj.getUserName() + "\n";
  34. }
  35. XmlFileBuilder ::~XmlFileBuilder()
  36. {
  37. }
  38. void XmlFileBuilder::buildHeader(DailyHeaderData& dailyheaderobj)
  39. {
  40.         m_strResult += "<?xml version="1.0" encoding="UTF-8" ?>\n";
  41.         m_strResult += "<DailyReport>\n";
  42.         m_strResult += "    <Header>\n";
  43.         m_strResult += "        <DepName>" + dailyheaderobj.getDepName() + "</DepName>\n";
  44.         m_strResult += "        <GenDate>" + dailyheaderobj.getExportDate() + "</GenDate>\n";
  45.         m_strResult += "    </Header>\n";
  46. }
  47. void XmlFileBuilder::buildBody(std::vector<DailyContentData*>& vec_dailycontobj)
  48. {
  49.         m_strResult += "    <Body>\n";
  50.         for (auto iter = vec_dailycontobj.begin(); iter != vec_dailycontobj.end(); ++iter)
  51.         {
  52.                 std::ostringstream oss;
  53.                 oss << (*iter)->getSpendTime();
  54.                 m_strResult += "        <Content>" + (*iter)->getContent() + "</Content>\n";
  55.                 m_strResult += "        <SpendTime>花费的时间:" + oss.str() + "小时" + "</SpendTime>\n";
  56.         }
  57.         m_strResult += "    </Body>\n";
  58. }
  59. void XmlFileBuilder::buildFooter(DailyFooterData& dailyfooterobj)
  60. {
  61.         m_strResult += "    <Footer>\n";
  62.         m_strResult += "        <UserName>报告人:" + dailyfooterobj.getUserName() + "</UserName>\n";
  63.         m_strResult += "    </Footer>\n";
  64.         m_strResult += "</DailyReport>\n";
  65. }
复制代码
固然,假如乐意,也可以继承实现JSON  格式文件以致是各种其他格式文件的导出,例 如创建一个JsonFileBuilder    类来实现JSON  格式文件的导出工作,干系代码可仿照上面的 代码自行扩展。
然后,实现一个文件指挥者类FileDirector,   代码如下:
  1. class FileDirector
  2. {
  3. public:
  4.         FileDirector(FileBuilder* ptmpBuilder);
  5.         std::string Construct(DailyHeaderData& dailyheaderobj, std::vector<DailyContentData*>& vec_dailycontobj, DailyFooterData& dailyfooterobj);
  6. private:
  7.         FileBuilder* m_pFileBuilder;
  8. };
  9. FileDirector::FileDirector(FileBuilder* ptmpBuilder)
  10. {
  11.         m_pFileBuilder = ptmpBuilder;
  12. }
  13. std::string FileDirector::Construct(DailyHeaderData& dailyheaderobj, std::vector<DailyContentData*>& vec_dailycontobj, DailyFooterData& dailyfooterobj)
  14. {
  15.         m_pFileBuilder->buildHeader(dailyheaderobj);
  16.         m_pFileBuilder->buildBody(vec_dailycontobj);
  17.         m_pFileBuilder->buildFooter(dailyfooterobj);
  18.         return m_pFileBuilder->GetResult();
  19. }
复制代码
请注意,在上个(创建怪物)范例中,复杂的对象或产物是指具体的怪物,这些具体的怪 物都继承自同一个父类(Monster   类),这不是必须的,即便是构建器子类创建相互之间没什 么关联关系的产物也完全可以。
在这个范例中,所导出的纯文本文件内容或 XML  文件内容就被看作一个复杂的对象 大概说成是产物(固然,在这个范例中并没有为这些产物创建单独的类),构建步调就是按照 拼接标题、拼接内容主体、拼接末端的序次举行,这个拼接步调是稳固的。看一看本范例的 制作者模式UML  图,如图3.9所示。
3.3.4  制作者模式的总结

通过上述两个范例,不丢脸到,制作者计划模式紧张用于分步调构建一个复杂的对象,此中构建步调是一个稳固的算法(构建算法),而复杂对象各个部门的创建则会有差别的变革。 在如下情况时,可以思量利用制作者模式:

  • 必要创建的产物对象内部结构复杂,产物通常由多个零部件构成。
  • 必要创建的产物对象内下属性相互依靠,必要指定创建序次。
  • 当创建复杂对象的步调(过程)应该独立于该对象的构成部门(通过引入指挥者类, 将创建步调封装在此中)。
  • 将复杂对象的创建和利用分离,使雷同的创建过程可以创建差别的产物。
制作者模式的核心要点在于将构建算法和具体的构建相互分离,如许构建算法就可以 被重用,通过编写差别的代码又可以很方便地对构建实现举行功能扩展。引入指挥者类后, 只要利用差别的天生器,利用雷同的构建过程就可以构建出差别的产物。
构建器接口界说的是怎样构建各个部件,也就是说,当必要创建具体部件的时间,交给 构建器来做。而指挥者有两个作用:

  • 负责通过部件以指定的序次来构建整个产物(控制了构建的过程)。
  • 指挥者通过提供Construct   接 口隔离了客户端(指main  主函数中的代码)与具体构 建过程必须要调用的类的成员函数之间的关联。
对于客户端,只必要知道各种具体的构建器以及指挥者的Construct   接口即可,并不需 要知道怎样构建具体的产物。想象一个项目开发小组,假如main  中构建产物的代码由普 通组员编写,这项工作天然比力轻松,但是,支持代码编写所运用的计划模式及实现一样平常是 由组长来完成,显然这项工作要复杂得多。
模板方法模式与制作者模式有类似之处,但模板方法模式紧张用来界说算法的骨架,把 算法中的某些步调延长到子类中去实现,模板方法模式接纳继承的方式来体现。在制作者 模式中,构建算法由指挥者来界说,具体部件的构建和装配工作由构建器实现,也就是说,该 模式接纳的是委托(指挥者委托给构建器)的方式来体现的。
工厂方法模式与制作者模式也有类似之处,但制作者模式偏重于一步步构建一个复杂 的产物对象,构建完成后返回所构建的产物,工厂方法模式偏重于多个产物对象(且对象所 属的类继承自同一个父类)的构建而无论产物本身是否复杂。
制作者模式具有如下优点:

  • 将一个复杂对象的创建过程封装起来。用同一个构建算法可以构建出体现上完全 差别的产物,实现产物构建和产物体现(表现)上的分离。 制作者模式也正是通过把 产物构建过程独立出来,从而才使构建算法可以被复用。如许的步调结构更轻易扩 展和复用。
  • 向客户端潜伏了产物内部的体现。
  • 产物的实现可以随时被更换(将差别的构建器提供给指挥者)。 制作者模式具有如下缺点:
  • 要求所创建的产物有比力多的共同点,创建步调(构成部门)要大抵雷同,假如产物 很不雷同,创建步调差异极大,则不恰当利用制作者模式,这是该模式利用范围受限 的地方。
  • 制作者模式涉及很多的类,比方必要组合指挥者和构建器对象,然后才气开始对象 的构建工作,这对于明白和学习是有肯定门槛的。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表