当前位置: 首页 > article >正文

设计模式之原型设计模式

一、原型设计模式概念

原型模式(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 方法才能被拷贝 ;

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


http://www.kler.cn/a/291858.html

相关文章:

  • 基于Java Springboot编程语言在线学习平台
  • 李秀贤主演警匪片《蓝色霹雳火》
  • 论文笔记(五十六)VIPose: Real-time Visual-Inertial 6D Object Pose Tracking
  • 鸿蒙中如何实现图片拉伸效果
  • Linux从0——1之shell编程4
  • 串口DMA接收不定长数据
  • 列转行-lateral view explode列转行
  • SQLAlchemy:Python SQL工具包和对象关系映射器
  • Kubernetes 简介与部署全攻略
  • 我使用了ChatGPT中的DALL-E创建了7张时尚客厅图片
  • 如何理解进程和线程之间的关系
  • 基于PHP评论区的存储型XSS漏洞
  • 中间件解析漏洞(附环境搭建教程)
  • Rust :也谈Json与转换
  • Harmony南向驱动开发: HDC与真机调试
  • Java【数组】
  • Docker占用根目录/存储空间过多如何清理?
  • windows@命令行安装国内软件@scoop和winget安装国内软件
  • 设计模式—2—单例模式
  • 【C++ 第二十章】模拟实现 shared_ptr(可以拷贝的智能指针)
  • 【Ubuntu20.04】配置深度学习环境
  • 机器学习周报(8.26-9.1)
  • 【C++ Primer Plus习题】9.3
  • C# 删除Word文档中的段落
  • Golang 内存泄漏详解:原因、检测与修复
  • 【论文阅读】LLM4CP: Adapting Large Language Models for Channel Prediction(2024)