本文讲述了对象创建模式中的原型模式,介绍了原型模式的动机、定义、结构、代码实例,最后进行了总结。
再次感谢GeekBand的李建忠老师、GOF等前辈
1. “对象创建”模式
- 通过“对象创建” 模式绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作。
- 典型模式
- Factory Method
- Abstract Factory
- Prototype 原型模式
- Builder
2. 动机(Motivation)
- 在软件系统中,经常面临着“某些结构复杂的对象[经过复杂处理之后的对象、状态发生变化的对象、中间对象、无法简单初始化就能创建出来的对象]”的创建工作;由于需求的变化,这些对象经常面临着剧烈的变化[对象状态发生变化],但是它们却拥有比较稳定一致的接口。
- 如何应对这种变化?如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象”,从而使得“依赖这些易变对象的客户程序”不随着需求的变化而变化?
3. 代码实例
下述代码和工厂方法中使用了相同的例子,即“文件分割”,在创建对象时,要避免在MainForm
中直接使用new
创建堆对象,即Isplitter *splitter = new FileSplitter()
代码中等号后边属于实现细节,在抽象模块中依赖实现细节,违背了“依赖倒置”原则,对象创建模块中的设计模式都是为了解决这个问题。
但前边学习的工厂方法模式和抽象工厂模式只能简单的创建新对象,一旦想要创建一个和当前对象具有相同状态的对象,就显得力不从心了。
先解决几个问题:
- 为什么必须要创建新对象,而不是直接通过参数传递,并在类中维护一个对象,然后使用它?注意这个模块的分类为“对象创建”,需求就是如何创建不同类型的临时对象,而不是维护已有对象,后者可能改变对象状态。
- 为什么不可以在抽象模块中通过拷贝构造来产生新对象?由于面向接口思想的实现,使得抽象模块中维护的是类的抽象接口,即使通过赋值,创建的也是父类对象,而不是我们想要的子类对象。
需求“创建一个和当前对象具有相同状态的对象”,可以使用拷贝构造函数(深拷贝),再结合工厂模式,即可得到原型模式,如下:
// Client.cpp
class MainForm : public Form
{
ISplitter* prototype;//原型对象
public:
MainForm(ISplitter* prototype){
this->prototype=prototype;
}
void Button1_Click(){
ISplitter * splitter=
prototype->clone(); //克隆原型
splitter->split();
}
};
注意原型模式中的原型不能直接使用,必须用来拷贝。通过虚克隆方法来实现动态克隆。
// Prototype.cpp
//抽象类
class ISplitter{
public:
virtual void split()=0;
virtual ISplitter* clone()=0; //通过克隆自己来创建对象
virtual ~ISplitter(){}
};
// ConcretePrototype.cpp
//具体类
class BinarySplitter : public ISplitter{
public:
virtual ISplitter* clone(){
return new BinarySplitter(*this);
}
};
class TxtSplitter: public ISplitter{
public:
virtual ISplitter* clone(){
return new TxtSplitter(*this);
}
};
class PictureSplitter: public ISplitter{
public:
virtual ISplitter* clone(){
return new PictureSplitter(*this);
}
};
class VideoSplitter: public ISplitter{
public:
virtual ISplitter* clone(){
return new VideoSplitter(*this);
}
};
4. 模式定义
使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象。
工厂模式和原型模式的区别:
- 使用工厂模式只能创建简单的对象,而使用原型模式可以创建和当前对象具有相同状态的对象,也就是说使用原型模式,你传入的是对象是什么状态,那么我之后克隆出来的就是这个状态。
- 工厂模式需要额外的常见工厂类,每一个子类都对应一个工厂,而原型模式只需要为该对象提供一个克隆方法即可。
- 原型模式是否可以替换工厂方法模式?因为只需要提供合适的构造参数。
5. 结构
绿色表示稳定,红色表示变化。Client
相当于MainForm
,Prototype
相当于ISplitter
,ConcretePrototype1/2
相当于 FileSplitter
、BinarySplitter
等,通过clone()
来实现动态克隆。
6. 要点总结
- Prototype模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些“易变类”拥有“稳定的接口。
- Prototype模式对于“如何创建易变类的实体对象”采用“原型克隆”的方法来做,它使得我们可以非常灵活地动态创建“拥有某些稳定接口的新对象一一所需工作仅仅是注册一个新类的对象(即原型),然后在任何需要的地方Clone。
- Prototype模式中的Clone方法可以利用某些框架中的序列化来实现深拷贝。