设计模式之原型设计模式

一、原型设计模式概念

原型模式(Prototype)是一种创建型设计模式, 使你能够复制已有对象, 而又无需使代码依赖它们所属的类。

原型模式主要用于对象的复制,它的核心是就是类图中的原型类 Prototype。Prototype 类需要具备以下两个条件:

  • 实现 Cloneable 接口。在 java 语言有一个 Cloneable 接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用 clone 方法。在 java 虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出 CloneNotSupportedException 异常。
  • 重写 Object 类中的 clone 方法。Java 中,所有类的父类都是 Object 类,Object 类中有一个 clone 方法,作用是返回对象的一个拷贝,但是其作用域 protected 类型的,一般的类无法调用,因此,Prototype 类需要将 clone 方法的作用域修改为 public 类型。
    我们复习一下C++中的浅拷贝与深拷贝:

浅拷贝是指当对象的字段值被复制时,字段引用的对象不会被复制。

例如:如果一个对象有一个指向字符串的字段,并且我们对该对象做了一个浅拷贝,那麽两个对象将引用同一个字符串。

深拷贝是指当一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝。
适用场景

  • 如果你需要复制一些对象, 同时又希望代码独立于这些对象所属的具体类, 可以使用原型模式。
  • 如果子类的区别仅在于其对象的初始化方式, 那么你可以使用该模式来减少子类的数量。 别人创建这些子类的目的可能是为了创建特定类型的对象

原型设计模式的结构

  1. 原型 (Prototype) 接口将对克隆方法进行声明。 在绝大多数情况下, 其中只会有一个名为 clone克隆的方法。
  2. 具体原型 (Concrete Prototype) 类将实现克隆方法。 除了将原始对象的数据复制到克隆体中之外, 该方法有时还需处理克隆过程中的极端情况, 例如克隆关联对象和梳理递归依赖等等。
  3. 客户端 (Client) 可以复制实现了原型接口的任何对象
  4. 克隆形状:生成完全相同的几何对象副本,同时无需代码与对象所属类耦合。

代码及类图如下:

问题:希望复制一个状态完全相同的对象。首先,新建一个相同类的对象。然后,复制所有成员变量。但是有时候不知道具体类型,而且成员变量可能是私有的。

解决方案:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。即复制已有对象,而无需使代码依赖他们所属的类。

#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 方法才能被拷贝 ;

② 深拷贝 与 浅拷贝 风险 : 克隆对象时进行的一些修改 , 容易出错 ; 需要灵活运用深拷贝与浅拷贝操作 ;

相关推荐
van叶~2 分钟前
算法妙妙屋-------1.递归的深邃回响:二叉树的奇妙剪枝
c++·算法
Half-up2 分钟前
C语言心型代码解析
c语言·开发语言
金池尽干2 分钟前
设计模式之——观察者模式
观察者模式·设计模式
knighthood200112 分钟前
解决:ros进行gazebo仿真,rviz没有显示传感器数据
c++·ubuntu·ros
也无晴也无风雨16 分钟前
代码中的设计模式-策略模式
设计模式·bash·策略模式
Source.Liu23 分钟前
【用Rust写CAD】第二章 第四节 函数
开发语言·rust
monkey_meng24 分钟前
【Rust中的迭代器】
开发语言·后端·rust
余衫马27 分钟前
Rust-Trait 特征编程
开发语言·后端·rust
monkey_meng30 分钟前
【Rust中多线程同步机制】
开发语言·redis·后端·rust
Jacob程序员32 分钟前
java导出word文件(手绘)
java·开发语言·word