一、原型模式简介
1、原型模式简介
原型模式使用原型实例指定创建对象的种类,并且通过拷贝原型对象创建新的对象。
Prototype模式提供了一个通过已存在对象进行新对象创建的接口(clone), clone()实现和具体的语言相关,在C++中通过拷贝构造函数实现。
原型模式实际上就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节。在初始化的信息不发生变化的情况下,克隆是最好的办法,既隐藏了对象创建的细节,又大大提高了性能。因为如果不用clone,每次new都需要执行一次构造函数,如果构造函数的执行时间很长,那么多次的执行初始化操作就太低效了。
原型模式实现clone接口的时候必须使用深拷贝。
原型模式重点在从自身赋值自己创建新的类对象,隐藏创建的细节。
2、原型模式角色
抽象原型(Prototype)角色:规定了具体原型对象必须实现的接口(如果要提供深拷贝,则必须具有实现clone的规定)
具体原型(ConcretePrototype):从抽象原型派生而来,是客户程序使用的对象,即被复制的对象,需要实现抽象原型角色所要求的接口。
客户(Client)角色:使用原型对象的客户程序
3、原型模式优缺点
优点:
A、原型模式对客户隐藏了具体的产品类
B、运行时刻增加和删除产品: 原型模式允许只通过客户注册原型实例就可以将一个新的具体产品类并入系统。
C、改变值以指定新对象: 高度动态的系统允许通过对象复合定义新的行为。如通过为一个对象变量指定值并且不定义新的类。通过实例化已有类并且将实例注册为客户对象的原型,就可以有效定义新类别的对象。客户可以将职责代理给原型,从而表现出新的行为。
D、改变结构以指定新对象:许多应用由部件和子部件来创建对象。
E、减少子类的构造,Prototype模式克隆一个原型而不是请求一个工厂方法去产生一个新的对象。
F、用类动态配置应用 一些运行时刻环境允许动态将类装载到应用中。
G、使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
H、使用原型模式的另一个好处是简化对象的创建,使得创建对象很简单。
缺点:
原型模式的主要缺陷是每一个抽象原型Prototype的子类都必须实现clone操作,实现clone函数可能会很困难。当所考虑的类已经存在时就难以新增clone操作,当内部包括一些不支持拷贝或有循环引用的对象时,实现克隆可能也会很困难的。
4、原型模式使用场景
原型模式的主要思想是基于现有的对象克隆一个新的对象出来,一般是由对象的内部提供克隆的方法,通过clone方法返回一个对象的副本。
原型模式使用场景:
A、当一个系统应该独立于它的产品创建、构成和表示时,要使用原型模式
B、当要实例化的类是在运行时刻指定时,如通过动态装载
C、为了避免创建一个与产品类层次平行的工厂类层次时
D、当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆原型可能比每次用合适的状态手工实例化原型类更方便一些。
5、浅拷贝与深拷贝
A、浅拷贝
被拷贝对象的所有变量都含有与原对象相同的值,而且对其他对象的引用仍然是指向原来的对象。即浅拷贝只负责当前对象实例,对引用的对象不做拷贝。
B、深拷贝
被拷贝对象的所有的变量都含有与原来对象相同的值,除了引用其他对象的变量。引用其他对象的变量将指向一个被拷贝的新对象,而不再是原有被引用对象。即深拷贝把要拷贝的对象所引用的对象也都拷贝了一次。
深拷贝要深入到多少层,是一个不确定的问题。在决定以深拷贝的方式拷贝一个对象的时候,必须决定对间接拷贝的对象是采取浅拷贝还是深拷贝还是继续采用深拷贝。因此,在采取深拷贝时,需要决定多深才算深。此外,在深拷贝的过程中,很可能会出现循环引用的问题。
二、原型模式实现
Prototype原型基类:
#ifndef PROTOTYPE_H
#define PROTOTYPE_H
#include <iostream>
//Prototype原型基类,定义Clone接口函数
class Prototype
{
public:
//定义clone接口,根据不同的派生类来实例化对象
virtual Prototype* clone() const = 0;
virtual ~Prototype()
{
std::cout << "Prototype::~Prototype()" << std::endl;
}
protected:
Prototype()
{
std::cout << "Prototype::Prototype()" << std::endl;
}
};
#endif // PROTOTYPE_H
ConcretePrototypeA原型派生类:
#ifndef CONCRETEPROTOTYPEA_H
#define CONCRETEPROTOTYPEA_H
#include "Prototype.h"
//派生自Prototype,实现其接口函数
class ConcretePrototypeA : public Prototype
{
public:
ConcretePrototypeA()
{
std::cout << "ConcretePrototypeA::ConcretePrototypeA()" << std::endl;
}
~ConcretePrototypeA()
{
std::cout << "ConcretePrototypeA::~ConcretePrototypeA()" << std::endl;
}
//拷贝构造函数
ConcretePrototypeA(const ConcretePrototypeA&)
{
std::cout << "ConcretePrototypeA::ConcretePrototypeA(const ConcretePrototypeA&)" << std::endl;
}
//实现基类定义的Clone接口,内部调用拷贝构造函数实现复制功能
virtual ConcretePrototypeA* clone() const
{
return new ConcretePrototypeA(*this);
}
};
#endif // CONCRETEPROTOTYPEA_H
ConcretePrototypeB原型派生类:
#ifndef CONCRETEPROTOTYPEB_H
#define CONCRETEPROTOTYPEB_H
#include "Prototype.h"
//派生自Prototype,实现其接口函数
class ConcretePrototypeB : public Prototype
{
public:
ConcretePrototypeB()
{
std::cout << "ConcretePrototypeB::ConcretePrototypeB()" << std::endl;
}
~ConcretePrototypeB()
{
std::cout << "ConcretePrototypeB::~ConcretePrototypeB()" << std::endl;
}
//拷贝构造函数
ConcretePrototypeB(const ConcretePrototypeB&)
{
std::cout << "ConcretePrototypeB::ConcretePrototypeB(const ConcretePrototypeB&)" << std::endl;
}
//实现基类定义的Clone接口,内部调用拷贝构造函数实现复制功能
virtual ConcretePrototypeB* clone() const
{
return new ConcretePrototypeB(*this);
}
};
#endif // CONCRETEPROTOTYPEB_H
客户调用程序:
#include "Prototype.h"
#include "ConcretePrototypeA.h"
#include "ConcretePrototypeB.h"
int main()
{
Prototype* p1 = new ConcretePrototypeA();
Prototype* p2 = p1->clone();
Prototype* p3 = new ConcretePrototypeA();
Prototype* p4 = p3->clone();
delete p1;
delete p2;
delete p3;
delete p4;
return 0;
}