一,原型模式的定义
原型模式是一种创建型设计模式,它允许通过克隆已有对象来创建新对象,从而无需调用显式的实例化过程。
原型模式的设计,使得它可以创建一个与原型对象相同或类似的新对象,同时又可以减少对象实例化操作产生的性能开销,使得创建对象的操作更加便捷,它减少了大量不必要的重复工作,并提高了系统性能。
当创建对象的操作比较复杂和耗时的时候,原型模式则提供了一个更加高效和简单的创建对象的模式,它可以更加快速的创建对象的副本,且不需要依赖对象的某些实例化步骤,它避免了使用传统的new关键字创建对象实例时的复杂构造过程。
原型模式的主要缺点则是原型对象必须预先存在于系统中,并且需要预先进行注册。此时,如果有大量的原型对象需要被创建,并且每个原型对象都需要进行自定义,维护和管理这些原型对象可能会变得很复杂。
原型模式在现实生活中的抽象实例:
图形绘制:假设我们需要绘制不同形状的图形,可以定义一个图形类作为原型,然后通过克隆该原型对象来创建具体的图形对象。
陶艺制作:先制作一个陶艺原型作为参考,然后通过复制或克隆原型来制作出多个相似的陶艺品。
服装设计:设计师预先设计一套成衣样板,然后通过复制或克隆样板来制作出多件相似或不同款式的服装。
二,原型模式的结构
原型模式主要包含以下组件:
1.抽象原型(Prototype):定义克隆方法的接口,具体原型类通过实现这些方法来提供其自身的副本。
2.具体原型(ConcretePrototype):包含抽象原型类的具体实现,它会提供一个复制自身的方法,该方法将创建并返回一个对象副本。
3.客户端(Client):客户端使用原型类来创建新对象的副本,它会先获取原型对象,并使用原型对象的克隆方法来创建新的对象实例。
组件之间的工作步骤如下:
1.客户端通过实例化具体原型类,并调用其克隆方法来创建一个原型对象。
2.原型对象调用自身的克隆方法,将自身复制一份,返回一个克隆的对象。
3.客户端获取到克隆对象后,可以根据自身的业务需求对其进行进一步的修改和使用。
对应UML类图:
三,原型模式代码样例
Demo1:
#include <iostream>
#include <string>
//定义原型基类
class Prototype {
public:
virtual ~Prototype() {}
virtual Prototype* clone() const = 0;
virtual void print() const = 0;
};
//定义具体原型类
class ConcretePrototype : public Prototype{
private:
std::string name;
public:
ConcretePrototype(const std::string& name) : name(name) {}
Prototype* clone() const override {
return new ConcretePrototype(*this);
}
void print() const override {
std::cout << "Prototype: " << name << std::endl;
}
};
int main() {
//创建原型对象
ConcretePrototype prototype("Original");
//使用原型对象创建新对象
Prototype* clone = prototype.clone();
clone->print();
//释放内存
delete clone;
return 0;
}
运行结果:
Prototype: Original
Demo2:
#include <iostream>
#include <string>
class Prototype {
public:
virtual ~Prototype() {}
virtual Prototype* clone() const = 0;
virtual void info() const = 0;
};
class ConcretePrototypeA: public Prototype {
public:
ConcretePrototypeA(int id, std::string name)
: m_id(id), m_name(name) {}
Prototype* clone() const override {
return new ConcretePrototypeA(*this);
}
void info() const override {
std::cout << "ConcretePrototypeA: id = "
<< m_id << ", name = "
<< m_name << std::endl;
}
private:
int m_id;
std::string m_name;
};
class ConcretePrototypeB: public Prototype {
public:
ConcretePrototypeB(std::string description)
: m_description(description) {}
Prototype* clone() const override {
return new ConcretePrototypeB(*this);
}
void info() const override {
std::cout << "ConcretePrototypeB: description = "
<< m_description << std::endl;
}
private:
std::string m_description;
};
int main() {
ConcretePrototypeA* prototypeA = new ConcretePrototypeA(1, "First");
Prototype* cloneA = prototypeA->clone();
cloneA->info();
ConcretePrototypeB* prototypeB = new ConcretePrototypeB("This is a prototype");
Prototype* cloneB = prototypeB->clone();
cloneB->info();
delete prototypeA;
delete cloneA;
delete prototypeB;
delete cloneB;
return 0;
}
运行结果:
ConcretePrototypeA: id = 1, name = First
ConcretePrototypeB: description = This is a prototype
四,原型模式的应用场景
图形用户界面:创建可定制的控件,如Windows的对话框,设计一个原型控件,让用户根据需求选择属性进行定制。
文本编辑器开发:支持“剪切”、“复制”、“粘贴”的功能,使用已存在的文档作为复制和创建新文档的原型。
数据库开发:将数据库的某个状态视为原型,当需要创建新的数据库版本时,可以直接从这个原型复制。
配置工具开发:基于原型模式帮助快速生成配置对象,而无需每次都新建一个空的配置。
Web应用程序:让用户可以在原型基础上添加、修改字段,动态生成表单元素。
五,原型模式的优缺点
原型模式的优点:
简化了对象的创建过程,使得代码更加简洁且易于维护。
提高了动态创建对象和销毁对象的效率。
封装和隐藏了创建对象的细节。
减少了对构造函数的直接调用,提高了代码的性能。
支持灵活的定制具体对象的属性和方法。
原型模式的缺点:
需要实现克隆对象的接口。
需要配合深拷贝或浅拷贝来使用,可能会导致引用对象的复制。
有的原型模式基于递归的方式来克隆对象,可能会引起堆栈溢出的问题。
针对大型对象的复制,会占用特别多的内存。
六,代码实战
Demo1:基于智能指针封装的原型模式:
#include <memory>
#include <iostream>
class Prototype {
public:
virtual std::unique_ptr<Prototype> clone() const = 0;
void printValue() const {
std::cout << "Origin Value." << std::endl;
}
};
class ConcretePrototype : public Prototype {
private:
int value;
public:
ConcretePrototype(int v) : value(v) {}
std::unique_ptr<Prototype> clone() const override {
return std::make_unique<ConcretePrototype>(value);
}
void printValue() const {
std::cout << "Value: " << value << std::endl;
}
};
int main() {
auto prototype = std::make_unique<ConcretePrototype>(5);
auto clonedPrototype = prototype->clone();
prototype->printValue();
clonedPrototype->printValue();
return 0;
}
运行结果:
Value: 5
Origin Value.
Demo2:基于工厂的方式管理各种原型
#include <iostream>
#include <string>
#include <map>
class Prototype {
public:
int data;
std::string name;
Prototype(int data, const std::string& name) : data(data), name(name) {}
virtual Prototype* clone() {
return new Prototype(*this);
}
};
class PrototypeFactory {
private:
std::map<std::string, Prototype*> prototypes;
public:
PrototypeFactory() {
prototypes["original"] = new Prototype(40, "Original");
prototypes["copy"] = new Prototype(100, "Copy");
}
Prototype* create(const std::string& type) {
return prototypes[type]->clone();
}
};
int main() {
PrototypeFactory factory;
Prototype* original = factory.create("original");
Prototype* copy = factory.create("copy");
std::cout << "Original: data="
<< original->data
<< ", name="
<< original->name << std::endl;
std::cout << "Copy: data="
<< copy->data
<< ", name="
<< copy->name << std::endl;
delete original;
delete copy;
return 0;
}
运行结果:
Original: data=40, name=Original
Copy: data=100, name=Copy
七,参考阅读
https://softwarepatterns.com/cpp/prototype-software-pattern-cpp-example
https://www.geeksforgeeks.org/prototype-design-pattern/
https://www.tutorialspoint.com/design_pattern/prototype_pattern.html
https://sourcemaking.com/design_patterns/prototype
https://softwareparticles.com/design-patterns-prototype/