Prototype 原型模式
动机 Motivation
- 在软件系统中,经常面临着“某些结构复杂的对象”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口。
- 如何应对这种变化?如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象”,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变?
引例
- 前文中分割器的例子,使用工厂方法模式是一种方式,下面介绍使用原型模式的方式。
// 抽象类
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方法可以利用某些框架中的序列化来实现深拷贝。