原型模式(Prototype) -- 对象创建型模式

意图

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

适用性

当 一个系统应该独立于它产品的创建、构成和表示时:
* 当要实例化的类是在运行时指定的。
* 为了避免创建一个和产品类层次平行的工厂类层次。
* 当一个类的实例只有几种不同状态组合中的一种;建立相应数目的原型并克隆他们可能比每次用合适的状态手工实例化该类更方便一点。

结构

Prototype

  • Protoytpe
    声明一个克隆自身的接口。
  • ConcretePrototype
    实现一个克隆自身的操作。
  • Client
    让一个原型克隆自身从而创建一个新的对象。

协作

客户请求一个原型克隆自身。

效果

Prototype有很多与Abstract Factory、Buidler一样的效果:对客户隐藏了具体的产品类,减少了耦合,客户无需改变即可使用与特定应用相关的类。

  • 优点
    1) 在运行时增加和删除产品。Prototype允许只通过客户注册的原型实例就可以将一个新的具体产品类并入系统。它比其他创建型模式更为灵活。
    2) 改变值以指定新的对象。我们可以实现一个可以通过实例化已有类并且将这些实例注册为客户对象的原型,就可以有效创建新类别的对象的功能。其实客户是将创建对象的职责代理给原型,从而可以表现出新的行为,不同的原型可以定义不同的克隆行为。
    3) 改变结构以指定新对象。许多应用由部件和子部件来构建一个对象。例如电路设计编辑器可以由子电路结构来构成一个新的对象。我们可以将一个子电路作为一个原型增加到电路元素选择板中,然后通过拷贝就可以生成这个子部件,然后用相同方式创建不同的子部件,最后合成一个我们需要的电路对象。
    4) 减少子类的构造。Factory Method经常产生一个与产品类层次平行的Creator类。Prototype模式使得你克隆一个原型而不是请求一个工厂方法产生一个新的对象,因此你根本不需要Creator类。
    5) 用类动态配置应用。一些运行时环境允许你动态的将类装载到应用中,在这种环境中,应用是不能静态引用类的构造器,而应该由运行环境在载入时自动创建每个类的实例,并用原型管理器来注册这个实例,然后应用就可以向原型管理器请求克隆新装载类的实例了。

  • 缺点
    每一个Prototype子类必须实现Clone操作,这可能对一些类是很困难的。例如当所考虑的类已经存在,或者类内部有一些不支持拷贝或循环引用的对象。

实现

像C++这样的静态语言中,类不是对象,并且运行时只能得到很少或者得不到任何类型的信息,Prototype就很有用了。而有一些语言本身就使用类似原型的东西(类对象)来创建类的实例,Prototype对他们来说就是固有的属性了。

当实现原型时,需要考虑以下一些问题:

  • 使用一个原型管理器
    当一个系统中的原型数目不固定时(也就是说,我们可以动态创建和销毁),要保持一个可用原型的注册表。客户不用自己来管理原型,可用在注册表中存储和检索原型。客户在克隆一个原型前会向注册表请求该原型。我们称这个注册表为原型管理器。
    原型管理器是一个关联存储器,它返回一个与给定关键字相匹配的原型。它有一些操作可用用来通过关键字注册原型和解除注册。客户可以在运行时更改或浏览这个注册表。这使得客户无需编写代码就可以扩展并得到系统清单。
  • 实现克隆操作
    Prototype模型最困难的部分在于正确的实现Clone操作。当对象结构包含循环引用时,这会尤为棘手。
    C++提供了拷贝构造器,但需要注意浅拷贝和深拷贝的问题,C++缺省实现是按成员拷贝,这意味所有的指针将会是共享对象的。而克隆一个结构复杂的原型通常需要深拷贝,来保证复制对象和原对象相互独立,因此必须自己来实现深拷贝。
  • 初始化克隆对象
    一些客户可能希望使用他们所选择的一些参数来初始化克隆对象或所有的内部状态。一般来说不可能在Clone操作中传递这些值,因为这些值的数目会因原型的类不同而有所不同。在Clone操作中传递参数会破坏克隆接口的统一性。
    可能原型的类已经定义好了一些可以设置关键状态值得操作,如果这样,客户在克隆后可以马上使用这些操作来改变内部状态。否则,我们可能需要引入一个Initialize操作,该操作使用初始化参数来设定克隆对象的内部状态。

示例

// 使用原型模式实现抽象工厂
#pragma once

#include "AbstractFactory.hpp"

//带Clone的Door
class Door : public MapSite
{
public:
    Door(const Door& other)
    {
        m_r1 = other.m_r1;
        m_r2 = other.m_r2;
    }
    void Initialize(Room* r1, Room* r2)
    {
        m_r1 = r1;
        m_r2 = r2;
    }
    Door* Clone() const
    {
        return new Door(*this);
    }
private:
    Room * m_r1;
    Room * m_r2;
};

// Wall等类...

class MazePrototypeFactory
{
public:
    MazePrototypeFactory(Maze* m, Wall* w, Room* r, Door* d)
    {
        m_prototypeMaze = m;
        m_prototypeWall = w;
        m_prototypeRoom = r;
        m_prototypeDoor = d;
    }

    virtual Wall* MakeWall() const
    {
        return m_prototypeWall->Clone();
    }
    virtual Door* MakeDoor(Room* r1, Room* r2) const
    {
        Door* door = m_prototypeDoor->Clone();
        door->Initialize(r1, r2);
        return door;
    }
    // ...
private:
    Maze* m_prototypeMaze;
    Room* m_prototypeRoom;
    Wall* m_prototypeWall;
    Door* m_prototypeDoor;
};

相关模式

Prototype和Abstract Factory模式在某些方面是相互竞争的。但他们也可以一起使用。Abstract Factory 可以存储一个被克隆的原型的集合,并且返回产品对象。
大量使用Composite 和Decorator模型的设计通常也可从Prototype模式处获益。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值