设计模式:原型模式(Prototype)
- 设计模式:原型模式(Prototype)
- 模式动机
- 模式定义
- 模式结构
- 时序图
- 模式实现
- 在单线程环境下的测试
- 在多线程环境下的测试
- 模式分析
- 优缺点
- 适用场景
- 应用场景
- 模式扩展
- 应用实例
- 实例 1:矩形对象克隆
- 实例 2:图形原型注册表
- 参考
设计模式:原型模式(Prototype)
原型模式(Prototype)属于创建型模式(Creational Pattern)的一种。
创建型模式(Creational Pattern)对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离。为了使软件的结构更加清晰,外界对于这些对象只需要知道它们共同的接口,而不清楚其具体的实现细节,使整个系统的设计更加符合单一职责原则。
创建型模式在创建什么(What),由谁创建(Who),何时创建(When)等方面都为软件设计者提供了尽可能大的灵活性。创建型模式隐藏了类的实例的创建细节,通过隐藏对象如何被创建和组合在一起达到使整个系统独立的目的。
模式动机
如果你有一个对象, 并希望生成与其完全相同的一个复制品, 你该如何实现呢? 首先, 你必须新建一个属于相同类的对象。 然后, 你必须遍历原始对象的所有成员变量, 并将成员变量值复制到新对象中。
然而,并非所有对象都能通过这种方式进行复制, 因为有些对象可能拥有私有成员变量, 它们在对象本身以外是不可见的。
直接复制还有另外一个问题。 因为你必须知道对象所属的类才能创建复制品, 所以代码必须依赖该类。 即使你可以接受额外的依赖性, 那还有另外一个问题: 有时你只知道对象所实现的接口, 而不知道其所属的具体类, 比如可向方法的某个参数传入实现了某个接口的任何对象。
通过复制现有的实例来创建新的实例,无需知道相应类的信息。这就是原型模式(Prototype)的模式动机。
模式定义
原型模式(Prototype)属于创建型模式。
原型模式将克隆过程委派给被克隆的实际对象。 模式为所有支持克隆的对象声明了一个通用接口, 该接口让你能够克隆对象, 同时又无需将代码和对象所属类耦合。
模式结构
原型模式(Prototype)包含如下角色:
- 抽象原型类(Prototype):接口定义了一个抽象的克隆方法。
- 具体原型类(Concrete Prototype):实现抽象原型类(接口)定义的克隆方法,提供一个具体的克隆方法来复制自己。
- 客户端(Client):使用原型类的对象来实现具体的操作,即通过复制原型对象来创建新的对象。
时序图
略。
模式实现
抽象原型类 Prototype.h:
#ifndef _PROTOTYPE_H_
#define _PROTOTYPE_H_
class Prototype
{
public:
virtual Prototype* clone() = 0;
};
#endif // !_PROTOTYPE_H_
具体原型类 ConcretePrototype1.h:
#ifndef _CONCRETE_PROTOTYPE_1_H_
#define _CONCRETE_PROTOTYPE_1_H_
#include "Prototype.h"
#include <string>
class ConcretePrototype1 : public Prototype
{
private:
std::string m_strTypeName;
public:
ConcretePrototype1(std::string name) : m_strTypeName(name) {};
// 拷贝构造函数
ConcretePrototype1(const ConcretePrototype1& rhs)
{
m_strTypeName = rhs.m_strTypeName;
}
Prototype* clone() override
{
return new ConcretePrototype1(*this);
}
std::string getTypeName() const
{
return m_strTypeName;
}
};
#endif // !_CONCRETE_PROTOTYPE_1_H_
具体原型类 ConcretePrototype2.h:
#ifndef _CONCRETE_PROTOTYPE_2_H_
#define _CONCRETE_PROTOTYPE_2_H_
#include "Prototype.h"
#include <string>
class ConcretePrototype2 : public Prototype
{
private:
std::string m_strTypeName;
public:
ConcretePrototype2(std::string name) : m_strTypeName(name) {};
// 拷贝构造函数
ConcretePrototype2(const ConcretePrototype2& rhs)
{
m_strTypeName = rhs.m_strTypeName;
}
Prototype* clone() override
{
return new ConcretePrototype2(*this);
}
std::string getTypeName() const
{
return m_strTypeName;
}
};
#endif // !_CONCRETE_PROTOTYPE_2_H_
在单线程环境下的测试
测试代码:
#include <iostream>
#include <stdlib.h>
#include "ConcretePrototype1.h"
#include "ConcretePrototype2.h"
using namespace std;
int main()
{
ConcretePrototype1* p1 = new ConcretePrototype1("A");
ConcretePrototype2* p2 = (ConcretePrototype2*)p1->clone();
cout << p1->getTypeName() << endl;
cout << p2->getTypeName() << endl;
delete p1;
delete p2;
system("pause");
return 0;
}
运行结果:
在多线程环境下的测试
略。
模式分析
- 原型模式将克隆过程委派给被克隆的实际对象。 模式为所有支持克隆的对象声明了一个通用接口, 该接口让你能够克隆对象, 同时又无需将代码和对象所属类耦合。 通常情况下, 这样的接口中仅包含一个克隆方法。
- 所有的类对克隆方法的实现都非常相似。该方法会创建一个当前类的对象, 然后将原始对象所有的成员变量值复制到新建的类中。 你甚至可以复制私有成员变量, 因为绝大部分编程语言都允许对象访问其同类对象的私有成员变量。
- 支持克隆的对象即为原型。 当你的对象有几十个成员变量和几百种类型时, 对其进行克隆甚至可以代替子类的构造。
优缺点
优点:
- 可以克隆对象,而无需与它们所属的具体类相耦合。
- 可以克隆预生成原型,避免反复运行初始化代码,在创建大量对象时可以节省时间和资源,提高了对象创建的效率。
- 可以隐藏对象创建和初始化的复杂性,可以更方便地生成复杂对象,并且更容易管理和维护。
- 可以用继承以外的方式来处理复杂对象的不同配置。
- 可以在运行时动态添加和删除对象。
- 可以保护原始对象,防止意外修改对原对象产生影响。
缺点:
- 必须保证原始对象和克隆对象之间的区别,否则可能会产生副作用。
- 克隆包含循环引用的复杂对象可能会非常麻烦。
- 原型模式需要给对象添加一个克隆方法。但是,该方法可能不适用于所有对象类型,例如具有命令行参数的程序。
适用场景
在以下情况下可以使用原型模式:
- 如果你需要复制一些对象,同时又希望代码独立于这些对象所属的具体类,可以使用原型模式。
- 如果子类的区别仅在于其对象的初始化方式,那么你可以使用该模式来减少子类的数量。 别人创建这些子类的目的可能是为了创建特定类型的对象。
在原型模式中,你可以使用一系列预生成的、 各种类型的对象作为原型。客户端不必根据需求对子类进行实例化, 只需找到合适的原型并对其进行克隆即可。
应用场景
- 细胞有丝分裂:原始细胞就是一个原型,它在复制体的生成过程中起到了推动作用。
- 孙悟空的七十二变。
- Object类:Java中的所有类都直接或间接继承自Object类,它提供了一个clone()方法,允许对象在克隆时使用它们的原型对象。
- Spring框架:在Spring框架中,原型范围bean使用原型模式。例如,在Spring中,可以将作用域设置为prototype,来创建一个bean的多个独立实例,这样每次在容器中注入bean时,将创建新的实例。
模式扩展
原型注册表 (Prototype Registry)提供了一种访问常用原型的简单方法,其中存储了一系列可供随时复制的预生成对象。 最简单的注册表原型是一个 名称 → 原型 的哈希表。 但如果需要使用名称以外的条件进行搜索,你可以创建更加完善的注册表版本。
应用实例
实例 1:矩形对象克隆
公司正在开发一个图形设计软件,其中有一个常用的图形元素是矩形。设计师在工作时可能需要频繁地创建相似的矩形,而这些矩形的基本属性(如颜色、宽度、高度)相同,但具体的位置可能不同。为了提高设计师的工作效率,请你使用原型模式设计一个矩形对象的原型。该原型可以根据用户的需求进行克隆,生成新的矩形对象。
#include <iostream>
#include <string>
#include <stdlib.h>
using namespace std;
// 抽象原型类
class Prototype
{
public:
virtual Prototype *clone() = 0;
};
// 具体的矩阵类
class RectanglePrototype : public Prototype
{
// 成员属性
private:
string m_color;
int m_height;
int m_width;
int x_pos;
int y_pos;
// 成员函数
public:
RectanglePrototype(string color, int height, int width) : m_color(color), m_height(height), m_width(width)
{
x_pos = 0;
y_pos = 0;
};
// 重载克隆函数接口
Prototype *clone() override
{
return new RectanglePrototype(*this);
}
// 信息打印
void PrintInfo()
{
cout << "Color: " << m_color << ", Height: " << m_height << ", Width: " << m_width
<< ", X pos: " << x_pos << ", Y pos: " << y_pos << endl;
}
void setPosition(int x, int y)
{
x_pos = x;
y_pos = y;
}
};
int main()
{
string color = "Red";
int height = 300, width = 100;
// 创建原型对象
RectanglePrototype *prototypeRectangle = new RectanglePrototype(color, height, width);
prototypeRectangle->PrintInfo();
// 克隆对象
RectanglePrototype *clonedRectangle = (RectanglePrototype *)prototypeRectangle->clone();
clonedRectangle->setPosition(50, 50);
clonedRectangle->PrintInfo();
delete clonedRectangle;
clonedRectangle = nullptr;
delete prototypeRectangle;
prototypeRectangle = nullptr;
system("pause");
return 0;
}
运行结果:
实例 2:图形原型注册表
#include <iostream>
#include <string>
#include <unordered_map>
#include <stdlib.h>
using namespace std;
// 抽象原型类
class Prototype
{
public:
virtual Prototype *clone() = 0;
virtual void draw() = 0;
};
// 具体的矩阵类
class RectanglePrototype : public Prototype
{
// 成员属性
private:
string m_strTypeName;
// 成员函数
public:
RectanglePrototype(string name) : m_strTypeName(name){};
// 重载克隆函数接口
Prototype *clone() override
{
return new RectanglePrototype(*this);
}
void draw() override
{
cout << "Inside Rectangle::draw() method." << endl;
}
void setName(const string &name)
{
m_strTypeName = name;
}
string getName() const
{
return m_strTypeName;
}
};
// 具体的正方形类
class SquarePrototype : public Prototype
{
// 成员属性
private:
string m_strTypeName;
// 成员函数
public:
SquarePrototype(string name) : m_strTypeName(name){};
// 重载克隆函数接口
Prototype *clone() override
{
return new SquarePrototype(*this);
}
void draw() override
{
cout << "Inside Square::draw() method." << endl;
}
void setName(const string &name)
{
m_strTypeName = name;
}
string getName() const
{
return m_strTypeName;
}
};
// 具体的圆形类
class CirclePrototype : public Prototype
{
// 成员属性
private:
string m_strTypeName;
// 成员函数
public:
CirclePrototype(string name) : m_strTypeName(name){};
// 重载克隆函数接口
Prototype *clone() override
{
return new CirclePrototype(*this);
}
void draw() override
{
cout << "Inside Circle::draw() method." << endl;
}
void setName(const string &name)
{
m_strTypeName = name;
}
string getName() const
{
return m_strTypeName;
}
};
class PrototypeRegistry
{
private:
unordered_map<string, Prototype *> items;
public:
void addItem(string id, Prototype *p)
{
items[id] = p;
}
Prototype *getById(string id)
{
return items[id]->clone();
}
};
int main()
{
RectanglePrototype *rect = new RectanglePrototype("Rectangle");
SquarePrototype *square = new SquarePrototype("Square");
CirclePrototype *circle = new CirclePrototype("Circle");
PrototypeRegistry *prototypeRegistry = new PrototypeRegistry();
prototypeRegistry->addItem("1", rect);
prototypeRegistry->addItem("2", square);
prototypeRegistry->addItem("3", circle);
RectanglePrototype *clonedRect = (RectanglePrototype *)prototypeRegistry->getById("1");
clonedRect->setName("my rectangle");
cout << clonedRect->getName() << endl;
clonedRect->draw();
delete prototypeRegistry;
delete rect;
delete square;
delete circle;
delete clonedRect;
system("pause");
return 0;
}
运行结果:
参考
- https://design-patterns.readthedocs.io/zh-cn/latest/creational_patterns/creational.html
- https://www.runoob.com/design-pattern/prototype-pattern.html
- https://blog.csdn.net/weixin_45433817/article/details/131037102
- https://blog.csdn.net/weixin_45433817/article/details/131095164
- https://refactoringguru.cn/design-patterns/prototype
- https://www.cnblogs.com/ybqjymy/p/17534839.html
- https://www.jb51.net/article/55870.htm