大卫的Design Patterns学习笔记04:Prototype

一、概述
Prototype(原型)模式用于动态抽取当前对象运行时的状态,从自身构造出一个新的对象,即自身的拷贝(往往是深拷贝),如果你愿意,你可以叫它Clone模式。

二、结构
Prototype模式的结构如下图所示:

1:Prototype模式

三、应用
Prototype模式在需要拷贝的产品的类型需动态指定时经常被用到。对于类似绘图软件这样的以对象管理为主要目的的应用系统中,各元素往往需要支持动态拷贝动作,因此常常会运用Prototype模式。
与Builder模式等不同,Prototype的产品构建工作是由原型产品完成的,产品通过拷贝对象自身(深拷贝)来完成新产品的构建(从而无需创建一个与产品类层次平行的工厂类层次,扩展产品类型也不会影响到产品创建代码,由于存在一个间接层Director,Builder模式在扩展产品类型时一般也不会影响到客户代码,甚至是Director类的代码,Factory模式由于需要创建与产品类层次平行的工厂类层次,当产品类型发生变化时,产品创建代码不可避免会受到影响),而其它几个Creational Patterns是通过组装或直接创建其它产品来达到向上级应用提交新产品的目的。
在应用Prototype模式在有些情况下可以简单地被拷贝构造函数所替代,但Prototype模式的应用使得拷贝产品变得更加灵活而强大,由于跟构造函数一样,拷贝构造函数也不能是虚函数,这使得我们在很多情况下,尤其是ConcretePrototype内部组成不同的情况下无法使用拷贝构造函数来创建产品(除非根据待拷贝产品的type,使用type - switch来分别调用不同的构造函数),而Prototype模式可以使我们在无需考虑ConcretePrototype类型的情况下对对象进行拷贝,但在ConcretePrototype内部,有些情况下可以考虑将clone操作转发给拷贝构造函数来完成,如:

ConcretePrototype * ConcretePrototype ::clone ()  const
{

    return
 new ConcretePrototype ( * this  );
}


但是需要注意的是,在构造函数中安排过多的操作也不利于程序的维护,clone方法作为一个普通成员函数,在使用和维护上更方便(以抛出异常为例,如果你的构造函数抛出异常,将使得客户代码编写时麻烦重重,虽然可以这么做。此外,由于 new会自动检查内存耗尽情况,当内存耗尽时,构造函数会抛出std ::bad_alloc异常,但我们往往不会去处理该异常,与其让我们的程序被这个难得一见的异常弄得遍体鳞伤,还不如干脆在程序出现该异常时直接跟用户说Bye -bye。同样,一个异常已经够让人烦了,谁会愿意再加个自定义异常进去?)。
GDI +(Windows平台图形支持库)的Image类就提供了Clone方法,以便我们进行Image子类对象的动态拷贝,对于我们的应用开发,当需要支持产品的动态拷贝时,应认真考虑是否需要提供clone方法,以免客户代码必须通过type - switch来根据产品的确切类型创建新产品,加大类与类之间的耦合度。
虽然有以上原因使得我们在很多情况下需要考虑使用Prototype模式,为我们的类提供Clone方法,但从目前看到的很多情况来看,提供Clone方法只是为了提供一个更加明确的语义,对于很多人来讲,Clone方法比拷贝构造函数更容易理解。个人观点,GDI +的大多数类提供拷贝构造函数可能是为了方便使用其它编程语言的用户,同时,也为了以后扩展的方便。

四、优缺点
优点:
1
、通过复制自身来创建新产品,客户不知道需要对象的实际类型,只需知道它的抽象基类即可;
2
、Prototype模式的应用使得扩展产品类型变得变得比较容易,不会影响到其它产品,甚至是客户代码;
3
、当我们需要拷贝一个包含多种元素(同一基类)的容器时,我们需要做的工作将变得十分简单。

缺点:必须先有一个对象实例(即原型)才能clone(所以,clone模式并不能成为其它创建型模式的更好的替代品)。

五、扩展
Prototype模式可以进一步扩展成带PrototypeManager的Prototype模式,在这种情况下,我们先使用PrototypeManager来创建具体原型类的对象,并记录每一个被创建的对象,以后需要拷贝原型对象时也通过PrototypeManager来完成,这从一定程度上解决了在clone前必须已经有一个对象实例的问题。

六、举例
Prototype模式并不是简简单单一个clone方法,Prototype模式的意义在于动态抽取当前对象运行时的状态,同时通过提供统一的clone接口方法,使得客户代码可以在不知道对象具体类型时仍然可以实现对象的拷贝,而无需运用type - switch检测对象的类型信息来分别调用创建方法来创建一个新的拷贝。
Prototype模式常常被用于与Flyweight模式(利用Prototype模式构造享元对象的拷贝)、State模式(利用Prototype模式构造状态类对象的拷贝,以供在对象间传递这些状态信息)、Strategy模式(与State模式下类似,利用Prototype模式构造Strategy类对象的拷贝,在不同的StrategyUser类间传递Strategy对象)等结合使用。

以某使用GDI +进行图片处理的程序为例,其中多个功能需要预览图片即将进行的处理(如Sharp、Darken、Rotate等)所能实现的效果,在用户没有最终确定该处理之前,需要复制一个当前Image对象的拷贝,然后对该拷贝进行处理,在创建该拷贝时存在一个问题,由于我们使用的是基类Image的指针,因此存在前面所说的问题,不能直接调用拷贝构造函数来创建新对象,同时,也不可能跟创建原型对象一样从文件流建立对象,而且,对象当前所处的状态是经过处理的,与创建之初的状态并不一样。为了便于我们解决类似这样的问题,Image类提供了一个Clone方法,以便我们进行对象的拷贝,Clone方法是虚基类Image的虚方法,由各子类具体实现。
下面的示例简单模拟了Image类的实现策略:

#include <iostream>
#include <string>
#include <vector>
using namespace std ;

struct
 Image
{

    virtual
 Image * clone () =  0 ;
    virtual
 void reform () { cout  <<  "reforming Image["  <<  this  <<  "]"  << endl ; }
};


class
 Bitmap  :  public Image
{

public
:
    Bitmap ( const string & filename ) : filename (filename ) {}
    Bitmap ( const Bitmap & pic ) : filename (pic .filename ) {}
    
    Image * clone () {  return  new Bitmap (* this ); }  // to simplify code, just call copy-ctor to create a clone
private :
    string filename ;
    // other attributes
};

class
 OtherPicFormat  :  public Image
{

public
:
    OtherPicFormat ( const string & filename ,  int otherinitialoption ) :
        filename (filename ), otherinitialoption (otherinitialoption ) {}
    OtherPicFormat ( const OtherPicFormat & pic ) :
        filename (pic .filename ), otherinitialoption (pic .otherinitialoption ) {}
        
    Image * clone () {  return  new OtherPicFormat (* this ); }  // to simplify code, just call copy-ctor to create a clone
private :
    string filename ;
    int
 otherinitialoption ;
    // other attributes
};

int
 main ()
{

    Image * ba [] = { new Bitmap ( "abc" ),  new OtherPicFormat ( "cde" ,  0 )};
    vector <Image *> vb ;
    copy (ba , ba  +  sizeof (ba ) /  sizeof (Image *), back_inserter (vb ));

    Image * pb  = NULL ;
    for
 (vector <Image *>::iterator it  = vb .begin (); it  != vb .end (); ++it )
    {

        pb  = (*it )->clone ();  // clone will create a new object
        pb ->reform ();
        delete
 pb ;
        delete
 *it ;
    }

    
    return
 0 ;
}


参考:
1
、http : //blog.csdn.net/blackphoenix/archive/2002/08/19/14151.aspx
现代C++中的设计模式是用于对象重用的可重复性方法。设计模式是一种在不同情况下解决相似问题的经验总结,可以通过将问题解决方案的关键部分抽象出来,从而提供灵活性和可重用性。设计模式不是编程语言特定的功能,而是一种通用的方法论。 在现代C++中,有许多常用的设计模式可以用于对象的可重用性。以下是几个常见的设计模式示例: 1.单例模式:用于确保一个类只能创建一个实例,并提供对该实例的全局访问点。对于有些对象只需要一个实例的情况,单例模式可以确保该实例的唯一性,从而方便访问和管理。 2.工厂模式:用于创建对象的过程中封装创建逻辑,让客户端代码无需关心对象的具体创建细节。通过工厂模式,可以通过一个工厂类来创建对象,从而提供更高的灵活性和可扩展性。 3.观察者模式:用于对象之间的发布-订阅机制,让一个对象(主题)的状态发生变化时,能够通知并自动更新其他依赖于该对象的对象(观察者)。通过观察者模式,可以实现对象之间的松耦合和消息传递,提高对象的可重用性和可维护性。 4.适配器模式:用于将一个类的接口转换成客户端所期望的另一个接口。适配器模式可以解决接口不兼容的问题,从而使得原本不兼容的类能够一起工作,提高可重用性和互操作性。 5.策略模式:用于定义一系列算法/行为,并将其封装成独立的类,使得它们可以互相替换。策略模式可以在运行时根据需要动态切换算法/行为,从而提供更高的灵活性和可重用性。 这些设计模式都是在现代C++中常见且有用的重用性方法,可以根据具体的应用场景选择合适的设计模式来提高代码的可维护性、可扩展性和可重用性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值