设计模式 - 生成器_类图


生成器模式

生成器模式属于创建型设计模式,根据需要分步创建功能较多的对象。

场景

问题

假设要生产两款不同品牌的笔记本ThinkPad X 13和MateBook X Pro,笔记本的参数包括重量、内存、硬盘和CPU,且同一型号的笔记本参数会存在差异。满足开闭原则的基础下,设计此场景。

分析

此场景主要的对象为两款笔记本,其中内部包含各种参数。因此要构建具体的实例,需要初始化时,对内部成员一一进行初始化。这些初始化代码一般会放在构造函数中,更糟糕的是可能会散落在各种使用代码中,不易被察觉。传统的方法或许暂时可以满足需求,但是维护起来令人抓狂,且违背开闭原则。

概念

此时可引进生成器模式,满足以上的设计需求。为避免在客户端使用时凌乱的使用构造函数,可将笔记本封装成类,内部提供必备参数的设置接口。提供统一的生成接口供用户使用以创建需要的实例。

此处主要涉及到以下几个概念:

生成器

笔记本已经抽象为类了,只需要在使用时创建笔记本实例。但是这些代码放在散乱的主逻辑中是糟糕且难以维护的。因此可实现一个生成器专门用于做此类工作,其功能主要为设置固定的笔记本参数,返回该实例。

不同的笔记本参数肯定是不同的,为符合开闭原则,设计为一类笔记本对应一个生成器实例。因此将生成器抽象为基类,派生出各个具体的生成器子类。

指挥

为便于生成器更加灵活的使用,例如各个参数的设置顺序、某些参数不需要等。可设计一个实例用于指挥生成器的使用。

实现

类图

根据以上分析各类之间的关系,绘制出以下类图。

设计模式 - 生成器_c++_02生成器类图

使用方法

  • 增加新产品笔记本
    若为同型号不同配置(低/中/高配)笔记本,可在Cdirector::BuildComputer()中做逻辑,见源码BuildComputer(ENOTEBOOK_TYPE notebookType)
    若非同型号笔记本,则需CComputer派生出新笔记本的生成类,交由CDirector实例生成具体的笔记本实例。
  • 删除不使用的新产品实例
    只需屏蔽CDirector的调用即可。

源码

#include <string>#include <iostream>
using namespace std;
typedef enum { ALLOCATION_MIN = 0, ALLOCATION_NORMAL, ALLOCATION_HIGH, ALLOCATION_MAX} ENOTEBOOK_TYPE;
class CComputer{public: void SetName(string value) { mName = value; } string GetName() { return mName; } void SetWeight(string value) { mWeight = value; } string GetWeight() { return mWeight; } void SetMemary(string value) { mMemary = value; } string GetMemary() { return mMemary; } void SetHardDisk(string value) { mHardDisk = value; } string GetHardDisk() { return mHardDisk; } void SetCpu(string value) { mCpu = value; } string GetCpu() { return mCpu; }
private: string mName; string mWeight; string mMemary; string mHardDisk; string mCpu;};
class CComputerBuilder{public:
virtual void BuildName() = 0; virtual void BuildWeight() = 0; virtual void BuildMemary() = 0; virtual void BuildHardisk() = 0; virtual void BuildHardiskHigh() = 0; virtual void BuildCpu() = 0; virtual CComputer* GetComputer() = 0;};
class CThinkPadX13Builder : public CComputerBuilder{public:
CThinkPadX13Builder() { mThinkPadX13 = new CComputer(); }
~CThinkPadX13Builder() { delete mThinkPadX13; }
void BuildName() { mThinkPadX13->SetName("ThinkPad X13"); }
void BuildWeight() { mThinkPadX13->SetWeight("1.3Kg"); }
void BuildMemary() { mThinkPadX13->SetMemary("32GB"); }
void BuildHardisk() { mThinkPadX13->SetHardDisk("512G"); }
void BuildHardiskHigh() { mThinkPadX13->SetHardDisk("1TB"); }
void BuildCpu() { mThinkPadX13->SetCpu("I5-10210U"); }
CComputer *GetComputer() {return mThinkPadX13;}
private: CComputer* mThinkPadX13;};
class CMateBookXProBuilder : public CComputerBuilder{public: CMateBookXProBuilder() { mMateBookXPro = new CComputer(); }
~ CMateBookXProBuilder() { delete mMateBookXPro; }
void BuildName() { mMateBookXPro->SetName("MateBook X Pro"); }
void BuildWeight() { mMateBookXPro->SetWeight("1.33KG"); }
void BuildMemary() { mMateBookXPro->SetMemary("16G"); }
void BuildHardisk() { mMateBookXPro->SetHardDisk("1TB"); }
void BuildHardiskHigh() { mMateBookXPro->SetHardDisk("2TB"); }
void BuildCpu() { mMateBookXPro->SetCpu("i7-1165G7"); }
CComputer* GetComputer() { return mMateBookXPro; }
private: CComputer* mMateBookXPro;};
class CDirector{public: CDirector(CComputerBuilder *pBuilder) { mBuilder = pBuilder; }
void BuildComputer(ENOTEBOOK_TYPE notebookType) { mBuilder->BuildName(); mBuilder->BuildWeight(); mBuilder->BuildMemary(); mBuilder->BuildCpu();
if (notebookType == ALLOCATION_NORMAL) { mBuilder->BuildHardisk(); } else if (notebookType == ALLOCATION_HIGH) { mBuilder->BuildHardiskHigh(); } else { cout << "Error: No this type!" << endl; }
}
private: CComputerBuilder* mBuilder;};
static void show_params(CComputer *pComputer){ cout << "---" << pComputer->GetName() << "---" << endl; cout << "Weight: " << pComputer->GetWeight() << endl; cout << "Memary: " << pComputer->GetMemary() << endl; cout << "Hard disk: " << pComputer->GetHardDisk() << endl; cout << "Cpu: " << pComputer->GetCpu() << endl; cout << endl;}
int main(int argc, char *argv[]){ CThinkPadX13Builder *theComputerBuilder1 = new CThinkPadX13Builder(); CDirector *theDirector1 = new CDirector(theComputerBuilder1); theDirector1->BuildComputer(ALLOCATION_HIGH); CComputer *theComputer1 = theComputerBuilder1->GetComputer(); show_params(theComputer1); delete theDirector1; delete theComputerBuilder1;
CMateBookXProBuilder *theComputerBuilder2 = new CMateBookXProBuilder(); CDirector *theDirector2 = new CDirector(theComputerBuilder2); theDirector2->BuildComputer(ALLOCATION_HIGH); CComputer *theComputer2 = theComputerBuilder2->GetComputer(); show_params(theComputer2); delete theDirector2; delete theComputerBuilder2;
return 0;}


输出

---ThinkPad X13---Weight: 1.3KgMemary: 32GBHard disk: 1TBCpu: I5-10210U
---MateBook X Pro---Weight: 1.33KGMemary: 16GHard disk: 2TBCpu: i7-1165G7


总结

在软件设计时,主函数的代码尽量减少实例初始化的动作,将初始化的行为尽量封装起来供主函数调用。主函数大多为业务逻辑,过多的代码会影响美观及维护。

推荐文章

   C++设计模式 - 工厂模式

   C++常用设计模式


最后

用心感悟,认真记录,写好每一篇文章,分享每一框干货。

更多文章内容包括但不限于C/C++、Linux、开发常用神器等,可进入开源519公众号聊天界面回复“文章目录” 或者 菜单栏选择“文章目录”查看。源码获取,聊天输入本文章标题。

设计模式 - 生成器_初始化_03