设计模式之原型设计模式
一、原型设计模式概念
原型模式(Prototype)是一种创建型设计模式, 使你能够复制已有对象, 而又无需使代码依赖它们所属的类。
原型模式主要用于对象的复制,它的核心是就是类图中的原型类 Prototype。Prototype 类需要具备以下两个条件:
- 实现 Cloneable 接口。在 java 语言有一个 Cloneable 接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用 clone 方法。在 java 虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出 CloneNotSupportedException 异常。
- 重写 Object 类中的 clone 方法。Java 中,所有类的父类都是 Object 类,Object 类中有一个 clone 方法,作用是返回对象的一个拷贝,但是其作用域 protected 类型的,一般的类无法调用,因此,Prototype 类需要将 clone 方法的作用域修改为 public 类型。
我们复习一下C++中的浅拷贝与深拷贝:浅拷贝是指当对象的字段值被复制时,字段引用的对象不会被复制。
例如:如果一个对象有一个指向字符串的字段,并且我们对该对象做了一个浅拷贝,那麽两个对象将引用同一个字符串。
深拷贝是指当一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝。
适用场景:
- 如果你需要复制一些对象, 同时又希望代码独立于这些对象所属的具体类, 可以使用原型模式。
- 如果子类的区别仅在于其对象的初始化方式, 那么你可以使用该模式来减少子类的数量。 别人创建这些子类的目的可能是为了创建特定类型的对象
原型设计模式的结构:
- 原型 (Prototype) 接口将对克隆方法进行声明。 在绝大多数情况下, 其中只会有一个名为
clone
克隆的方法。- 具体原型 (Concrete Prototype) 类将实现克隆方法。 除了将原始对象的数据复制到克隆体中之外, 该方法有时还需处理克隆过程中的极端情况, 例如克隆关联对象和梳理递归依赖等等。
- 客户端 (Client) 可以复制实现了原型接口的任何对象
- 克隆形状:生成完全相同的几何对象副本,同时无需代码与对象所属类耦合。
代码及类图如下:
问题:希望复制一个状态完全相同的对象。首先,新建一个相同类的对象。然后,复制所有成员变量。但是有时候不知道具体类型,而且成员变量可能是私有的。
解决方案:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。即复制已有对象,而无需使代码依赖他们所属的类。
#include <iostream>
#include <unordered_map>
#include <string>
#include <vector>
using namespace std;
enum Type
{
ROBOT_CAT = 0,
ROBOT_DOG
};
class Robot
{
protected:
string m_prototype_name = "";
float m_stateOfCharge = 0;
public:
Robot() = default;
Robot(string name):m_prototype_name(name){}
virtual ~Robot(){};
virtual Robot* clone() const = 0;
virtual void setStateOfCharge(float) = 0;
};
class RobotCat :public Robot
{
protected:
float m_CatValue = 0;
public:
virtual ~RobotCat(){};
RobotCat(const RobotCat& robot)
{
m_CatValue = robot.m_CatValue;
}
RobotCat(string name, float value) :Robot(name), m_CatValue(value) {};
virtual Robot* clone() const override
{
return new RobotCat(*this);
}
virtual void setStateOfCharge(float value) override
{
m_stateOfCharge = value;
cout << "Cat charge is " << m_prototype_name << " " << m_stateOfCharge << " " << m_CatValue << endl;
}
};
class RobotDog :public Robot
{
protected:
float m_DogValue = 0;
public:
virtual ~RobotDog() {};
RobotDog(string name, float value) :Robot(name), m_DogValue(value) {};
virtual Robot* clone() const override
{
return new RobotDog(*this);
}
virtual void setStateOfCharge(float value) override
{
m_stateOfCharge = value;
cout << "Dog charge is " << m_prototype_name << " " << m_stateOfCharge << " " << m_DogValue << endl;
}
};
class CloneFactory
{
public:
unordered_map<Type, Robot*> m_prototypes;
CloneFactory()
{
m_prototypes[ROBOT_CAT] = new RobotCat("Cat", 1.0);
m_prototypes[ROBOT_DOG] = new RobotDog("Dog", 2.0);
}
~CloneFactory()
{
delete m_prototypes[ROBOT_CAT];
delete m_prototypes[ROBOT_DOG];
}
Robot* createRobot(Type type)
{
return m_prototypes[type]->clone();
}
};
void clintcode(CloneFactory& factory)
{
cout<<"Cat Clone:" << endl;
Robot* robot1 = factory.createRobot(ROBOT_CAT);
robot1->setStateOfCharge(0.5);
delete robot1;
Robot* robot3 = factory.createRobot(ROBOT_CAT);
robot3->setStateOfCharge(0.6);
delete robot3;
cout << "Dog Clone:" << endl;
Robot* robot2 = factory.createRobot(ROBOT_DOG);
robot2->setStateOfCharge(0.5);
delete robot2;
}
int main()
{
CloneFactory factory;
clintcode(factory);
return 0;
}
二、原型设计模式的优缺点
1 . 原型模式优点 : 性能高 , 简单 ;
① 性能高 : 使用原型模式复用的方式创建实例对象 , 比使用构造函数重新创建对象性能要高 ; ( 针对类实例对象开销大的情况 )② 流程简单 : 原型模式可以简化创建的过程 , 可以直接修改现有的对象实例的值 , 达到复用的目的 ; ( 针对构造函数繁琐的情况 )
2 . 原型模式缺点 : 实现复杂 , 坑多 ;
① 覆盖 clone 方法 ( 必须 ) : 必须重写对象的 clone 方法 , Java 中提供了 cloneable 标识该对象可以被拷贝 , 但是必须覆盖 Object 的 clone 方法才能被拷贝 ;② 深拷贝 与 浅拷贝 风险 : 克隆对象时进行的一些修改 , 容易出错 ; 需要灵活运用深拷贝与浅拷贝操作 ;