原型模式(创建型)
前言
在公司上班时候,每周都需要写工作周报,而写周报的时候,每次的格式都差不多,有时候想偷懒,就想自己弄个周报的模板,然后每次填充模板的内容,这样既提高了工作效率,又保证了格式的一致性,上级领导每次浏览周报的时候也一目了然了,真是一石二鸟–),这种可以看成原型模式的应用,即每次周报对象内容都大概相同,仅需要复制粘贴,然后修改下每周的差异内容即可。
原型模式
原型模式,顾名思义,就是有个对象的原型,然后再通过此原型对象来创建新的对象。而这些新的对象,可以内容相同,也可以不同。
意图
用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象
参与者
AbstractPrototype
抽象原型类,声明一个克隆自身的接口ConcretePrototype
具体原型类,实现一个克隆自身的操作Client
让一个原型克隆自身从而创建一个新的对象
模式结构
代码实现
首先声明一个抽象的原型类
AbstractPrototype
,原型类中必须有clone()
接口(虚函数):1
2
3
4
5
6
7class AbstractPrototype
{
public:
AbstractPrototype() {};
virtual ~AbstractPrototype() {};
virtual AbstractPrototype* Clone() = 0; // 克隆接口,返回自身的类型
};然后分别实现两个具体的原型对象,实现其中的
clone()
接口(一般通过拷贝构造函数来实现):1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67// 具体原型对象1
class Prototype1: public AbstractPrototype
{
private:
int m_ValueTest; //浅拷贝 测试值
public:
Prototype1(int value): m_ValueTest(value) {};
Prototype1(const Prototype1& rhs) // 拷贝构造函数
{
m_ValueTest = rhs.m_ValueTest;
}
Prototype1* Clone() // 返回自己类类型
{
return new Prototype1(*this); // 通过调用拷贝构造函数来Clone对象
}
void ShowValue()
{
cout << m_ValueTest << endl;
}
};
class Prototype2: public AbstractPrototype
{
private:
int m_ValueTest; // 浅拷贝,一般的拷贝
char *m_pName; // 涉及到深拷贝
public:
Prototype2(int value, const char* pname)
{
m_ValueTest = value;
if (NULL == pname)
{
m_pName = new char[1];
m_pName[0] = '\0';
}
else
{
m_pName = new char[strlen(pname) + 1];
strcpy(m_pName, pname);
}
};
virtual ~Prototype2()
{
delete m_pName; // 释放内存
}
Prototype2(const Prototype2& rhs) // 拷贝构造函数
{
//浅拷贝:只会拷贝对象中的基本的数据类型
m_ValueTest = rhs.m_ValueTest;
//深拷贝:需要手动申请内存,对于数组、容器对象、引用(指针)对象
m_pName = new char[strlen(rhs.m_pName) + 1];
strcpy(m_pName, rhs.m_pName);
}
Prototype2* Clone() // 返回自己类类型
{
// 通过调用拷贝构造函数来Clone对象
Prototype2 *pP2 = new Prototype2(*this);
pP2->m_ValueTest++; //改变克隆的对象内部成员,克隆出不同m_ValueTest的对象
return pP2;
}
void ShowValue()
{
cout << m_ValueTest << endl;
cout << m_pName << endl;
}
};测试原型模式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19void PrototypeTest()
{
// 返回子类型的类指针,抽象类没有ShowValue方法
Prototype1 *pP1= new Prototype1(10);
Prototype1 *pPcopy= pP1->Clone(); // 克隆出新对象pPcopy
pP1->ShowValue(); // 10
pPcopy->ShowValue(); // 10
Prototype2 *pP2= new Prototype2(20, "Tly");
Prototype2 *pPcopy2= pP2->Clone();
pP2->ShowValue(); // 20, "Tly"
pPcopy2->ShowValue(); // 21 "Tly"
delete pP1; pP1 = NULL;
delete pPcopy; pPcopy = NULL;
delete pP2; pP2 = NULL;
delete pPcopy2; pPcopy2 = NULL;
};
运行结果:
10
10
20
Tly
21
Tly
使用场景
- 创建新对象成本较大,如初始化需要占用较长的时间、内存资源、网络资源等等,新的对象可以通过已有对象的复制得来,如是相似,则更改相应的属性成员变量即可
- 如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占用内存较少时,可以使用原型模式配合备忘录模式来实现
- 当我们的对象类型不是开始就能确定的,而这个类型是在运行期确定的话,那么我们通过这个类型的对象克隆出一个新的对象比较容易一些
- 重复地创建相似对象时可以考虑使用原型模式
- 创建对象时,构造函数的参数很多,而自己又不完全的知道每个参数的意义,就可以使用原型模式来创建一个新的对象,不必去理会创建的过程
优缺点
- 优点
- 当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率
- 扩展性较好,由于在原型模式中提供了抽象原型类,在客户端可以针对抽象原型类进行编程,而将具体原型类写在配置文件中,增加或减少产品类对原有系统都没有任何影响
- 可以使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用(如恢复到某一历史状态),可辅助实现撤销操作
- 缺点
- 需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了“开闭原则”
- 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较麻烦
与其他创建型模型区别
- 工厂方法模式、抽象工厂模式、建造者模式和原型模式都是创建型模式
- 工厂方法模式适用于生产较复杂,一个工厂生产单一的一种产品的时候
- 抽象工厂模式适用于一个工厂生产多个相互依赖的产品;
- 建造者模式着重于复杂对象的一步一步创建,组装产品的过程,并在创建的过程中,可以控制每一个简单对象的创建;
- 原型模式则更强调的是从自身复制自己,创建要给和自己一模一样的对象
原型模式具体实例
实现前言所描述的简单工作周报和工作月报原型,增加一个原型管理器来管理这两种原型
代码实现
声明一个抽象的原型类:
1
2
3
4
5
6
7
8
9
10
11// 抽象原型
class AbstractReport
{
public:
AbstractReport(){};
virtual ~AbstractReport() {};
public:
virtual AbstractReport* Clone() = 0; // 抽象克隆方法
virtual void display(void) {}; //用来显示
virtual void setReportTitle(string title) {}; //用来修改title
};分别实现两个具体的原型类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127// 周报具体原型
class WeeklyReport : public AbstractReport
{
private:
string _reportTitle;
int _weekNo;
string _name;
string _description;
char* _accessory;
public:
WeeklyReport(string title, int no, string name, string description, \
const char* accessory): _reportTitle(title), _weekNo(no), _name(name), _description(description)
{
if (NULL == accessory)
{
_accessory = new char[0];
_accessory[0] = '\0';
}
else
{
_accessory = new char[strlen(accessory) + 1];
strcpy(_accessory, accessory);
}
}
virtual ~WeeklyReport()
{
if (NULL != _accessory)
{
delete _accessory;
_accessory = NULL;
}
}
public:
WeeklyReport(WeeklyReport &rhs) //拷贝构造函数,浅拷贝 + 深拷贝
{
_reportTitle = rhs._reportTitle;
_weekNo = rhs._weekNo;
_name = rhs._name;
_description = rhs._description;
_accessory = new char[strlen(rhs._accessory) + 1]; // 深拷贝
memmove(_accessory, rhs._accessory, (strlen(rhs._accessory) + 1));
}
WeeklyReport* Clone() // 克隆接口
{
WeeklyReport *report = new WeeklyReport(*this);
return report;
}
void setReportTitle(string reportTitle)
{
_reportTitle = reportTitle;
}
void display(void)
{
cout << "weekly report title: " << _reportTitle << endl;
cout << "weekly no: " << _weekNo << endl;
cout << "reporter: " << _name << endl;
cout << "description: " << _description <<endl;
cout << "accessory: " << _accessory <<endl;
}
};
// 月报具体原型
class MonthlyReport : public AbstractReport
{
private:
string _reportTitle;
int _monthNo;
string _name;
string _description;
char* _accessory;
public:
MonthlyReport(string title, int no, string name, string description, \
const char* accessory): _reportTitle(title), _monthNo(no), _name(name), _description(description)
{
if (NULL == accessory)
{
_accessory = new char[0];
_accessory[0] = '\0';
}
else
{
_accessory = new char[strlen(accessory) + 1];
strcpy(_accessory, accessory);
}
}
virtual ~MonthlyReport()
{
if (NULL != _accessory)
{
delete _accessory;
_accessory = NULL;
}
}
public:
MonthlyReport(MonthlyReport &rhs) //拷贝构造函数,浅拷贝 + 深拷贝
{
_reportTitle = rhs._reportTitle;
_monthNo = rhs._monthNo;
_name = rhs._name;
_description = rhs._description;
_accessory = new char[strlen(rhs._accessory) + 1]; // 深拷贝
memmove(_accessory, rhs._accessory, (strlen(rhs._accessory) + 1));
}
MonthlyReport* Clone() // 克隆接口
{
MonthlyReport *report = new MonthlyReport(*this);
return report;
}
void setReportTitle(string reportTitle)
{
_reportTitle = reportTitle;
}
void display(void)
{
cout << "monthly report title: " << _reportTitle <<endl;
cout << "monthly no: " << _monthNo <<endl;
cout << "reporter: " << _name <<endl;
cout << "description: " << _description <<endl;
cout << "accessory: " << _accessory <<endl;
}
};定义一个原型管理器类,使用单例模式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35// Report Manager
class ReportManager
{
private:
map<string, AbstractReport*> reportMap;
private:
ReportManager(){};
ReportManager(const ReportManager &rhs) {};
ReportManager& operator = (const ReportManager &rhs) {};
public:
static ReportManager* getInstance(void)
{
static ReportManager reportMgr;
return &reportMgr;
}
public:
// 增加新的report
void addReport(string name, AbstractReport* report)
{
reportMap.insert(make_pair(name, report));
}
AbstractReport* getReport(string name) // 返回一个report副本
{
for (map<string, AbstractReport*>::iterator it = reportMap.begin(); \
it != reportMap.end(); ++it)
{
if (it->first == name)
{
return (it->second)->Clone(); // clone方法返回副本
}
}
return NULL;
}
};测试实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27void ReportPrototypeTest()
{
ReportManager *rm = ReportManager::getInstance();
WeeklyReport *wr =new WeeklyReport ("design model", 1, "tly", "design domain object model","no accessory");
MonthlyReport *mr =new MonthlyReport("complete design all model", 3, "tly", "complte design all domain object model","month accessory");
cout << "$$$$$$$$$$origin$$$$$$$$$$" << endl;
wr->display();
mr->display();
rm->addReport("week", wr);
rm->addReport("month", mr);
AbstractReport *wrCopy = rm->getReport("week");
AbstractReport *mrCopy = rm->getReport("month");
cout << "$$$$$$$$$$copy$$$$$$$$$$" << endl;
wrCopy->setReportTitle("change weekly report title"); //修改title
wrCopy->display();
mrCopy->display();
delete wr; wr = NULL;
delete mr; mr = NULL;
delete wrCopy; wrCopy = NULL;
delete mrCopy; mrCopy = NULL;
}
运行结果:
$$$$$$$$$$origin$$$$$$$$$$
weekly report title: design model
weekly no: 1
reporter: tly
description: design domain object model
accessory: no accessory
monthly report title: complete design all model
monthly no: 3
reporter: tly
description: complte design all domain object model
accessory: month accessory
$$$$$$$$$$copy$$$$$$$$$$
weekly report title: change weekly report title
weekly no: 1
reporter: tly
description: design domain object model
accessory: no accessory
monthly report title: complete design all model
monthly no: 3
reporter: tly
description: complte design all domain object model
accessory: month accessory