[C++]22种设计模式的C++实现大纲
原型是一种创建型设计模式,使你能够复制已有对象,而又无需使代码依赖它们所属的类。
克隆(Clone)。
如果你有一个对象,并希望生成与其完全相同的一个复制品,你该如何实现呢?首先,你必须新建一个属于相同类的对象。然后,你必须遍历原始对象的所有成员变量,并将成员变量值复制到新对象中。
不错!但有个小问题。并非所有对象都能通过这种方式进行复制,因为有些对象可能拥有私有成员变量,它们在对象本身以外是不可见的。
直接复制还有另外一个问题。因为你必须知道对象所属的类才能创建复制品,所以代码必须依赖该类。即使你可以接受额外的依赖性,那还有另外一个问题:有时你只知道对象所实现的接口,而不知道其所属的具体类,比如可向方法的某个参数传入实现了某个接口的任何对象。
原型模式将克隆过程委派给被克隆的实际对象。模式为所有支持克隆的对象声明了一个通用接口,该接口让你能够克隆对象, 同时又无需将代码和对象所属类耦合。通常情况下,这样的接口中仅包含一个“克隆”方法。
所有的类对“克隆”方法的实现都非常相似。该方法会创建一个当前类的对象,然后将原始对象所有的成员变量值复制到新建的类中。你甚至可以复制私有成员变量,因为绝大部分编程语言都允许对象访问其同类对象的私有成员变量。
支持克隆的对象即为原型。当你的对象有几十个成员变量和几百种类型时,对其进行克隆甚至可以代替子类的构造。
其运作方式如下:创建一系列不同类型的对象并不同的方式对其进行配置。如果所需对象与预先配置的对象相同,那么你只需克隆原型即可,无需新建一个对象。
原型注册表(Prototype Registry)提供了一种访问常用原型的简单方法,其中存储了一系列可供随时复制的预生成对象。最简单的注册表原型是一个「名称 → 原型」的哈希表。但如果需要使用名称以外的条件进行搜索,你可以创建更加完善的注册表版本。
● 如果你需要复制一些对象,同时又希望代码独立于这些对象所属的具体类,可以使用原型模式。
这一点考量通常出现在代码需要处理第三方代码通过接口传递过来的对象时。即使不考虑代码耦合的情况,你的代码也不能依赖这些对象所属的具体类,因为你不知道它们的具体信息。原型模式为客户端代码提供一个通用接口,客户端代码可通过这一接口与所有实现了克隆的对象进行交互,它也使得客户端代码与其所克隆的对象具体类独立开来。
● 如果子类的区别仅在于其对象的初始化方式,那么你可以使用该模式来减少子类的数量。别人创建这些子类的目的可能是为了创建特定类型的对象。
在原型模式中,你可以使用一系列预生成的、各种类型的对象作为原型。客户端不必根据需求对子类进行实例化,只需找到合适的原型并对其进行克隆即可。
● 你可以克隆对象,而无需与它们所属的具体类相耦合。
● 你可以克隆预生成原型,避免反复运行初始化代码。
● 你可以更方便地生成复杂对象。
● 你可以用继承以外的方式来处理复杂对象的不同配置。
克隆包含循环引用的复杂对象可能会非常麻烦。
● 在许多设计工作的初期都会使用工厂方法(较为简单,而且可以更方便地通过子类进行定制), 随后演化为使用抽象工厂、原型或生成器(更灵活但更加复杂)。
● 抽象工厂模式通常基于一组工厂方法,但你也可以使用原型模式来生成这些类的方法。
● 原型可用于保存命令的历史记录。
● 大量使用组合和装饰的设计通常可从对于原型的使用中获益。你可以通过该模式来复制复杂结构,而非从零开始重新构造。
● 原型并不基于继承,因此没有继承的缺点。另一方面,原型需要对被复制对象进行复杂的初始化。工厂方法基于继承,但是它不需要初始化步骤。
● 有时候原型可以作为备忘录的一个简化版本,其条件是你需要在历史记录中存储的对象的状态比较简单,不需要链接其他外部资源,或者链接可以方便地重建。
● 抽象工厂、生成器和原型都可以用单例来实现。
Prototype.h:
#ifndef PROTOTYPE_H_
#define PROTOTYPE_H_
// 抽象原型类
class Object {
public:
virtual Object* clone() = 0;
};
#endif // PROTOTYPE_H_
ConcretePrototype.h:
#ifndef CONCRETE_PROTOTYPE_H_
#define CONCRETE_PROTOTYPE_H_
#include
#include
#include "Prototype.h"
// 邮件的附件
class Attachment {
public:
void set_content(std::string content) {
content_ = content;
}
std::string get_content() {
return content_;
}
private:
std::string content_;
};
// 具体原型: 邮件类
class Email : public Object {
public:
Email() {}
Email(std::string text, std::string attachment_content) : text_(text), attachment_(new Attachment()) {
attachment_->set_content(attachment_content);
}
~Email() {
if (attachment_ != nullptr) {
delete attachment_;
attachment_ = nullptr;
}
}
void display() {
std::cout << "------------查看邮件------------" << std::endl;
std::cout << "正文: " << text_ << std::endl;
std::cout << "邮件: " << attachment_->get_content() << std::endl;
std::cout << "------------查看完毕------------" << std::endl;
}
// 深拷贝
Email* clone() override {
return new Email(this->text_, this->attachment_->get_content());
}
void changeText(std::string new_text) {
text_ = new_text;
}
void changeAttachment(std::string content) {
attachment_->set_content(content);
}
private:
std::string text_;
Attachment *attachment_ = nullptr;
};
#endif // CONCRETE_PROTOTYPE_H_
main.cpp:
#include "ConcretePrototype.h"
#include
int main() {
Email* email = new Email("最初的文案", "最初的附件");
Email* copy_email = email->clone();
copy_email->changeText("新文案");
copy_email->changeAttachment("新附件");
std::cout << "original email:" << std::endl;
email->display();
std::cout << "copy email:" << std::endl;
copy_email->display();
delete email;
delete copy_email;
}
编译运行:
$g++ -g main.cpp -o prototype -std=c++11
$./prototype
original email:
------------查看邮件------------
正文: 最初的文案
邮件: 最初的附件
------------查看完毕------------
copy email:
------------查看邮件------------
正文: 新文案
邮件: 新附件
------------查看完毕------------
[C++]22种设计模式的C++实现大纲跳转