当前位置:网站首页>原型模式(prototype)
原型模式(prototype)
2022-06-26 12:40:00 【baboon_chen】
一、什么是原型模式?
关于原型模式的介绍,网上已有不少好的文章进行讲解,图文并茂,生动形象。在此引用两篇非常不错的介绍,推荐大家:
1、 C++设计模式——原型模式 - Ring_1992 - 博客园 (cnblogs.com)
2、 原型设计模式 (refactoringguru.cn)
拿最简单的例子来说,一个考试系统中,每个考生的试卷内容相同,但是题目的次序,甚至选项的次序不同。通过一份原始的试卷A,能拷贝生成不同的试卷B、C、D。这就是原型模式的一个例子。类似的还有细胞的有丝分裂,由一个细胞分裂成两个相同的细胞,等等。它们都是通过现有的东西,再复制出另一个,这就是原型模式。
如果从实现的方面来说明原型模式的话,它的UML图如下:

由于克隆需要一个原型,而上面的类图中Prototype就这个原型,Prototype定义了克隆自身的Clone接口,由派生类进行实现,而实现原型模式的重点就在于这个Clone接口的实现。
ConcretePrototype1类和ConcretePrototype2类继承自Prototype类,并实现Clone接口,实现克隆自身的操作;比如考试系统中的试题可以实现Clone接口,在Clone中将选项的顺序打乱,而不改变选项内容。
同时,在ConcretePrototype1类和ConcretePrototype2类中需要重写拷贝构造函数,供Clone函数调用,Clone就是通过在内部调用重写拷贝构造函数实现的。在后续的编码过程中,如果某个类需要实现Clone功能,就只需要继承Prototype类,然后重写自己的拷贝构造函数就好了。
二、它有哪些优点?缺点?
优点
当我们的对象类型不是开始就能确定的,而这个类型是在运行期确定的话,那么我们通过克隆已有的对象比直接创建一个新的对象要更容易一些;
有的时候,我们需要一个对象在某个状态下的副本,此时,我们使用原型模式是最好的选择;例如:一个对象,经过一段处理之后,其内部的状态发生了变化(私有成员发生改变);这个时候,我们需要一个这个状态的副本,如果直接new一个新的对象的话,但是它的状态是不对的,此时,可以使用原型模式,将原来的对象拷贝一个出来,这个对象就和之前的对象是完全一致的了;
有的时候,创建对象时,构造函数的参数很多,而自己又不完全的知道每个参数的意义,就可以使用原型模式来创建一个新的对象,不必去理会创建的过程。
缺点
继承原型的子类都要实现Clone操作
clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
三、举例
1、示例程序
using std::string;
// Prototype Design Pattern
// 拷贝已有对象而不用依赖它们的构造函数。
enum Type {
PROTOTYPE_1 = 0,
PROTOTYPE_2
};
/**
* 接口类提供克隆功能,提供Method方法打印不同类型的输出值打印。
*/
class Prototype {
protected:
string prototype_name_;
float prototype_field_;
public:
Prototype() {}
Prototype(string prototype_name)
: prototype_name_(prototype_name) {
}
virtual ~Prototype() {}
virtual Prototype *Clone() const = 0;
virtual void Method(float prototype_field) {
this->prototype_field_ = prototype_field;
std::cout << "Call Method from " << prototype_name_ << " with field : " << prototype_field << std::endl;
}
};
/**
* ConcretePrototype1是Prototype的一个子类,实现了克隆方法。
* 在这个例子中原型类的所有数据成员都在堆栈中,所以没有实现拷贝构造函数。
* 如果你有指针成员,如:String* name,你需要重写拷贝构造函数,实现深拷贝。
*/
class ConcretePrototype1 : public Prototype {
private:
float concrete_prototype_field1_;
public:
ConcretePrototype1(string prototype_name, float concrete_prototype_field)
: Prototype(prototype_name), concrete_prototype_field1_(concrete_prototype_field) {
}
/**
* 注意,Clone方法返回一个指针, 指向新开辟的空间,里面存入了ConcretePrototype1的复制品。
* 因此,client(调用clone方法)有责任释放那段内存。
* 如果你有智能指针的知识,你可以在这里使用unique_pointer。
*/
Prototype *Clone() const override {
return new ConcretePrototype1(*this);
}
};
class ConcretePrototype2 : public Prototype {
private:
float concrete_prototype_field2_;
public:
ConcretePrototype2(string prototype_name, float concrete_prototype_field)
: Prototype(prototype_name), concrete_prototype_field2_(concrete_prototype_field) {
}
Prototype *Clone() const override {
return new ConcretePrototype2(*this);
}
};
/**
* 在PrototypeFactory中, 有两个已经实例化的原型。你可以继续克隆出其它成员。
*/
class PrototypeFactory {
private:
std::unordered_map<Type, Prototype *, std::hash<int>> prototypes_;
public:
PrototypeFactory() {
prototypes_[Type::PROTOTYPE_1] = new ConcretePrototype1("PROTOTYPE_1 ", 50.f);
prototypes_[Type::PROTOTYPE_2] = new ConcretePrototype2("PROTOTYPE_2 ", 60.f);
}
/**
* 一定要小心内存分配,最好在这使用智能指针。
*/
~PrototypeFactory() {
delete prototypes_[Type::PROTOTYPE_1];
delete prototypes_[Type::PROTOTYPE_2];
}
/**
* 这里你只需要指定你想克隆哪个原型,就可以克隆相应的对象。
*/
Prototype *CreatePrototype(Type type) {
return prototypes_[type]->Clone();
}
};
void Client(PrototypeFactory &prototype_factory) {
std::cout << "Let's create a Prototype 1\n";
Prototype *prototype = prototype_factory.CreatePrototype(Type::PROTOTYPE_1);
prototype->Method(90);
delete prototype;
std::cout << "\n";
std::cout << "Let's create a Prototype 2 \n";
prototype = prototype_factory.CreatePrototype(Type::PROTOTYPE_2);
prototype->Method(10);
delete prototype;
}
int main() {
PrototypeFactory *prototype_factory = new PrototypeFactory();
Client(*prototype_factory);
delete prototype_factory;
return 0;
}
2、试题复制
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <algorithm>
class ProtoType {
public:
virtual ~ProtoType() {};
virtual ProtoType* Clone() = 0;
virtual void Show() = 0;
};
class QuestionA : public ProtoType {
public:
QuestionA() : problem("waht's your name?"), options({"dongdong", "youyou", "tutu", "beibei"}) {}
~QuestionA() {}
ProtoType* Clone() override {
ProtoType* q = new QuestionA(*this);
// 改变选项顺序
QuestionA* ptr = dynamic_cast<QuestionA*>(q);
random_shuffle(ptr->options.begin(), ptr->options.end());
return q;
}
void Show() override {
std::cout << problem << std::endl;
printf("A:%s \t B:%s \t C:%s \t D:%s \n",
options[0].c_str(), options[1].c_str(), options[2].c_str(), options[3].c_str());
}
private:
std::string problem;
std::vector<std::string> options;
};
int main() {
QuestionA q_a;
ProtoType* q_b = q_a.Clone();
q_a.Show();
q_b->Show();
delete q_b;
return 0;
}
边栏推荐
- Adobe Acrobat prevents 30 security software from viewing PDF files or there are security risks
- map 取值
- 四类线性相位 FIR滤波器设计 —— MATLAB源码全集
- Electron official docs series: Contributing
- Software testing - concept
- processing 随机生成线动画
- HDU 5860
- UVA5009 Error Curves三分
- Electron official docs series: Examples
- C - Common Subsequence
猜你喜欢

倍福PLC通过程序获取系统时间、本地时间、当前时区以及系统时间时区转换

Learning Processing Zoog
![P5733 [deep foundation 6. example 1] automatic correction](/img/34/081dbd805593a92a86c3081d6772e3.png)
P5733 [deep foundation 6. example 1] automatic correction
What should the software test report include? Interview must ask

Word document export (using fixed template)

倍福Ethercat模块网络诊断和硬件排查的基本方法

Do you know the limitations of automated testing?

Processing random generation line animation

C# const详解:C#常量的定义和使用

How does easygbs solve the abnormal use of intercom function?
随机推荐
National standard gb28181 protocol easygbs video platform TCP active mode streaming exception repair
使用SSH密钥对登陆服务器
桥接模式(Bridge)
Sinotech software outsourcing
D - 滑雪
ES6模块
利用scrapy爬取句子迷网站优美句子存储到本地(喜欢摘抄的人有福了!)
Stream learning record
el-form-item 包含两个input, 校验这两个input
软件测试测试常见分类有哪些?
倍福PLC通过程序获取系统时间、本地时间、当前时区以及系统时间时区转换
Adobe Acrobat prevents 30 security software from viewing PDF files or there are security risks
sql 将数据表b字段值赋值到数据表a中某一列
倍福PLC选型--如何看电机是多圈绝对值还是单圈绝对值编码器
Design of four kinds of linear phase FIR filters -- complete set of Matlab source code
Electron official docs series: Examples
goto语句实现关机小程序
倍福Ethercat模块网络诊断和硬件排查的基本方法
Photoshop 2022 23.4.1增加了哪些功能?有知道的吗
UVA10341 solve it 二分