生成器模式
生成器模式属于创建型设计模式,根据需要分步创建功能较多的对象。
场景
问题
假设要生产两款不同品牌的笔记本ThinkPad X 13和MateBook X Pro,笔记本的参数包括重量、内存、硬盘和CPU,且同一型号的笔记本参数会存在差异。满足开闭原则的基础下,设计此场景。
分析
此场景主要的对象为两款笔记本,其中内部包含各种参数。因此要构建具体的实例,需要初始化时,对内部成员一一进行初始化。这些初始化代码一般会放在构造函数中,更糟糕的是可能会散落在各种使用代码中,不易被察觉。传统的方法或许暂时可以满足需求,但是维护起来令人抓狂,且违背开闭原则。
概念
此时可引进生成器模式,满足以上的设计需求。为避免在客户端使用时凌乱的使用构造函数,可将笔记本封装成类,内部提供必备参数的设置接口。提供统一的生成接口供用户使用以创建需要的实例。
此处主要涉及到以下几个概念:
生成器
笔记本已经抽象为类了,只需要在使用时创建笔记本实例。但是这些代码放在散乱的主逻辑中是糟糕且难以维护的。因此可实现一个生成器专门用于做此类工作,其功能主要为设置固定的笔记本参数,返回该实例。
不同的笔记本参数肯定是不同的,为符合开闭原则,设计为一类笔记本对应一个生成器实例。因此将生成器抽象为基类,派生出各个具体的生成器子类。
指挥
为便于生成器更加灵活的使用,例如各个参数的设置顺序、某些参数不需要等。可设计一个实例用于指挥生成器的使用。
实现
类图
根据以上分析各类之间的关系,绘制出以下类图。
生成器类图使用方法
-
增加新产品笔记本
若为同型号不同配置(低/中/高配)笔记本,可在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.3Kg
Memary: 32GB
Hard disk: 1TB
Cpu: I5-10210U
---MateBook X Pro---
Weight: 1.33KG
Memary: 16G
Hard disk: 2TB
Cpu: i7-1165G7
总结
在软件设计时,主函数的代码尽量减少实例初始化的动作,将初始化的行为尽量封装起来供主函数调用。主函数大多为业务逻辑,过多的代码会影响美观及维护。
网友评论