一,原型模式的定义
原型模式是一种创建型设计模式,它允许通过克隆已有对象来创建新对象,从而无需调用显式的实例化过程。
原型模式的设计,使得它可以创建一个与原型对象相同或类似的新对象,同时又可以减少对象实例化操作产生的性能开销,使得创建对象的操作更加便捷,它减少了大量不必要的重复工作,并提高了系统性能。
当创建对象的操作比较复杂和耗时的时候,原型模式则提供了一个更加高效和简单的创建对象的模式,它可以更加快速的创建对象的副本,且不需要依赖对象的某些实例化步骤,它避免了使用传统的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/