“对象创建”模式之原型模式

目录

  • [Prototype 原型模式](#Prototype 原型模式)

Prototype 原型模式

动机 Motivation

  • 在软件系统中,经常面临着"某些结构复杂的对象"的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口。
  • 如何应对这种变化?如何向"客户程序(使用这些对象的程序)"隔离出"这些易变对象",从而使得"依赖这些易变对象的客户程序"不随着需求改变而改变?

引例

  • 前文中分割器的例子,使用工厂方法模式是一种方式,下面介绍使用原型模式的方式。
cpp 复制代码
// 抽象类
class ISplitter
{
public:
    virtual void split() = 0;
    virtual ISplitter* Clone() = 0;          // 通过克隆自己来创建对象
    virtual ~ISplitter() {}
};

// 具体类 - 二进制分割器
class BinarySplitter : public ISplitter
{
private:
    std::string* binaryData;  // 假设有需要深拷贝的指针成员
    int dataSize;
    
public:
    BinarySplitter() : binaryData(nullptr), dataSize(0) {}
    
    // 深拷贝构造函数
    BinarySplitter(const BinarySplitter& other) 
        : dataSize(other.dataSize)
    {
        if (other.binaryData != nullptr) 
        {
            binaryData = new std::string(*other.binaryData);
        } else 
        {
            binaryData = nullptr;
        }
    }
    
    ~BinarySplitter() 
    {
        delete binaryData;
    }
    
    void setData(const std::string& data) 
    {
        if (binaryData == nullptr) 
        {
            binaryData = new std::string(data);
        } else 
        {
            *binaryData = data;
        }
    }
    
    virtual void split() override 
    {
        std::cout << "Splitting binary data: " << (binaryData ? *binaryData : "") << std::endl;
    }
    
    virtual ISplitter* Clone() override 
    {
        return new BinarySplitter(*this);  // 调用拷贝构造函数进行深拷贝
    }
};

// 具体类 - 图片分割器
class PictureSplitter : public ISplitter
{
private:
    std::string* imageData;  // 假设有需要深拷贝的指针成员
    int width, height;
    
public:
    PictureSplitter() : imageData(nullptr), width(0), height(0) {}
    
    // 深拷贝构造函数
    PictureSplitter(const PictureSplitter& other) 
        : width(other.width), height(other.height)
    {
        if (other.imageData != nullptr) 
        {
            imageData = new std::string(*other.imageData);
        } else 
        {
            imageData = nullptr;
        }
    }
    
    ~PictureSplitter() 
    {
        delete imageData;
    }
    
    void setImage(const std::string& data, int w, int h) 
    {
        if (imageData == nullptr) 
        {
            imageData = new std::string(data);
        } else 
        {
            *imageData = data;
        }
        width = w;
        height = h;
    }
    
    virtual void split() override 
    {
        std::cout << "Splitting picture (" << width << "x" << height 
                  << "): " << (imageData ? imageData->substr(0, 10) + "..." : "") << std::endl;
    }
    
    virtual ISplitter* Clone() override 
    {
        return new PictureSplitter(*this);  // 调用拷贝构造函数进行深拷贝
    }
};

// 具体类 - 视频分割器
class VideoSplitter : public ISplitter
{
private:
    std::string* videoData;  // 假设有需要深拷贝的指针成员
    double duration;
    
public:
    VideoSplitter() : videoData(nullptr), duration(0) {}
    
    // 深拷贝构造函数
    VideoSplitter(const VideoSplitter& other) 
        : duration(other.duration)
    {
        if (other.videoData != nullptr) 
        {
            videoData = new std::string(*other.videoData);
        } else 
        {
            videoData = nullptr;
        }
    }
    
    ~VideoSplitter() 
    {
        delete videoData;
    }
    
    void setVideo(const std::string& data, double dur) 
    {
        if (videoData == nullptr) 
        {
            videoData = new std::string(data);
        } else 
        {
            *videoData = data;
        }
        duration = dur;
    }
    
    virtual void split() override 
    {
        std::cout << "Splitting video (" << duration 
                  << "s): " << (videoData ? videoData->substr(0, 10) + "..." : "") << std::endl;
    }
    
    virtual ISplitter* Clone() override 
    {
        return new VideoSplitter(*this);  // 调用拷贝构造函数进行深拷贝
    }
};

// MainForm
class MainForm
{
    ISplitter* prototype;
    
public:
    MainForm(ISplitter* prototype)          // 通常由外部传入
    {
        this->prototype = prototype;
    }
    
    ~MainForm() 
    {
        delete prototype;
    }
    
    void Button1_Click()
    {
        ISplitter* splitter = prototype->Clone();     // 克隆原型    
        splitter->split();
        delete splitter;
    }
};

int main() 
{
    // 创建原型对象
    BinarySplitter* binaryProto = new BinarySplitter();
    binaryProto->setData("Sample binary data");
    
    // 使用原型
    MainForm form1(binaryProto);
    form1.Button1_Click();
    
    // 另一个例子
    PictureSplitter* pictureProto = new PictureSplitter();
    pictureProto->setImage("Very long picture data...", 1920, 1080);
    
    MainForm form2(pictureProto);
    form2.Button1_Click();
    
    return 0;
}

模式定义

使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象。

结构 Structure

前文代码中的ISplitter对应图中的Prototype;BinarySplitter对应ConcretePrototype1,VideoSplitter对应ConcretePrototype2;MainForm对应Client

要点总结

  • Prototype原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些"易变类"拥有"稳定的接口"。
  • 原型模式对于"如何创建易变类的实体对象"采用"原型克隆"的方法来做,它使得我们可以非常灵活地动态创建"拥有某些稳定接口"的新对象------所需工作仅仅是注册一个新类的对象(即原型),然后在任何需要的地方Clone。
  • 原型模式中的Clone方法可以利用某些框架中的序列化来实现深拷贝。

来源:极客班------C++设计模式入门

相关推荐
杨充20 小时前
10.可测试性实战设计
设计模式·开源·代码规范
杨充20 小时前
9.重构十二式的实战
设计模式·开源·代码规范
杨充20 小时前
6.设计原则的全景图
设计模式·开源·全栈
杨充20 小时前
2.面向对象的特性
设计模式
杨充20 小时前
7.SOLID原则案例汇
设计模式·开源·全栈
杨充20 小时前
8.反模式与坏味道
设计模式·开源·代码规范
杨充20 小时前
3.接口vs抽象类比较
设计模式
咖啡八杯1 天前
文法、BNF与AST
java·设计模式·解释器模式·ast·文法
咖啡八杯1 天前
GoF设计模式——解释器模式
java·后端·spring·设计模式