第7章 创建型模式—原型模式

1. 原型模式(Prototype pattern)的定义

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

  ①通过克隆来创建新的对象实例

  ②新的对象实例复制原型实例属性的值

(2)原型模式的结构和说明

 

  ①Prototype:声明一个克隆自身的接口,用来约束想要克隆自己的类,要求他们都要实现这里定义的克隆方法。

  ②ConcretePrototype:实现Prototype接口的类,这些类真正实现了隆自身的功能。

  ③Client:使用原型的客户端,首先要获取到原型实例对象,然后通过原型实例的克隆自身来创建新的对象实例。

(3)思考原型模式

  ①原型模型的本质克隆生成对象

  ②原型模式可以用来解决“只知接口而不知实现的问题”,出现一种“接口造接口”的假象。

  ③原型模式的重心还是在创建新的对象实例。至于创建出来的对象,其属性的值是否一定要和原型对象完全一样,这并没有强制规定,但一般会拷贝成一样的。

  ④通过克隆出来实例是原型实例是两个完全独立的实例,他们之间没有关联。

【编程实验】订单拆分处理

 

 View Code

(4)原型模式的主要功能:通过克隆来创建新的对象实例。

  ①原型模式从某种意义上说,是new操作。但只是“类似于new”,而不是“就是new”。因为new一个对象实例,一般属性是没有值或只有默认值;而克隆一个实例,通常与原型对象的属性值是一样的。

  ②原型实例和克隆实例本质上是两个不同的实例,它们之间是没有关联的。即一个实例的属性值发生改变,不会影响另一个实例。

2. 浅度克隆和深度克隆

(1)浅度克隆:只负责克隆按值传递的数值

(2)深度克隆:除了浅度克隆要克隆的值外,还负责克隆指针所指对象的数据。

3. 原型模式的优缺点

(1)优点

  ①对客户端隐藏具体的实现类型:客户端只知道原型接口的类型,从而减少了客户端对这些具体实现类型的依赖。

  ②在运行时动态改变具体的实现类型:原型模式可以在运行期间,由客户来注册符合原型接口的实现类型,也可以动态地改变具体的实现类型。表面看起来接口没有任何变化,但其实运行的己经是另一个类实例了。因为克隆一个原型就类似于实例化一个类。

(2)缺点

  ①每个原型的子类都必须实现clone接口

  ②当原型实例中出现复杂对象时,会递归对克隆其他对象。

  ③当原型内部包括一些不支持拷贝的对象时,可以导致克隆失败。

4.原型模式的使用场景

(1)在创建对象的时候,我们不只是希望被创建的对象继承其基类的基本结构,还希望继承原型对象的数据

(2)希望对目标对象的修改不影响既有的原型对象(深度克隆的时候可以完全互不影响)。

(3)创建对象时,只知道接口,可以这克隆原型来得到。

(4)需要实例化的类是在运行时刻动态指定时,可以使用原型模式。

5. 原型模式的扩展

(1)需求分析

  ①在很多软件中都一个绘图工具箱,里面有直线、圆形、矩形等绘图工具。每点一次这个工具箱中的工具,则绘制一个相应的图。

  ②在软件实现中,可以先将这些图形生成一个个带有默认大小对象(“绘图工具”),并将他们放入一个叫原型管理器的东西中(类似于工具箱)。以后绘图时,可以从这个管理器中“拖出”(Clone)这些对象,并改变他们的属性值以达到绘图的目的。

(2)原型管理器中各角色

 

  ①客户(Client)角色:客户端类向原型管理器提出创建对象的请求。

  ②抽象原型(Prototype)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体原型类所需的接口。

  ③具体原型(Concrete Prototype)角色:被复制的对象。此角色需要实现抽象的原型角色所要求的接口。

  ④原型管理器(Prototype Manager)角色:创建具体原型类的对象,并记录每一个被创建的对象。

【编程实验】“绘图工具箱”的实现

复制代码
//创建型模式:原型模式
//原型管理器:

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

//抽象原型角色:Prototype
class DrawPrototype
{
protected:
    //需要绘制的图形名称、高和宽
    string m_DrawName;
    double m_Height;
    double m_Width;
    
public:
    virtual DrawPrototype* clone() = 0; //对原型进行克隆
    
    string& getDrawName(){return m_DrawName;}
    void setDrawName(string drawName){m_DrawName = drawName;}
    
    double getHeight(){return m_Height;}
    void setHeight(double height){m_Height = height;}
    
    double getWidth(){return m_Width;}
    void setWidth(double width){m_Width = width;}    
};

//具体原型角色(ConcretePrototype)
class ConcreteDrawing : public DrawPrototype
{
public:
    DrawPrototype* clone()
    {
        DrawPrototype* ret = new ConcreteDrawing;
        ret->setDrawName(this->getDrawName());
        ret->setHeight(this->getHeight());
        ret->setWidth(this->getWidth());
        
        return ret;      
    }   
    
    //显示自身特性
    void showInfo()
    {
        cout <<"DrawName=" <<m_DrawName << " Height=" << m_Height << 
                " Width=" << m_Width << endl;
    }    
};

//原型管理器角色(PrototypeManager)
class DrawManager
{
    map<string,DrawPrototype*> drawingMap;
public:
    //添加原型到管理器
    void addDrawing(string key,DrawPrototype* dpt)
    {
        drawingMap[key] = dpt;
    }
    
    //获取到对应名字的原供以供克隆副本
    DrawPrototype* getDrawing(string drawName)
    {
        return drawingMap[drawName];
    }    
};


//客户端角度(Client)
int main()
{
    //初始化绘画管理工具
    DrawManager drawManager;
    
    //初始化矩形、圆形、梯形、直线的原型实体以供后面拖出来使用
    
    //矩形
    DrawPrototype* rc = new ConcreteDrawing();
    rc->setDrawName("Rectangle");
    rc->setHeight(100);
    rc->setWidth(100);
    drawManager.addDrawing("Rectangle", rc);
    
    //圆形
    DrawPrototype* cc = new ConcreteDrawing();
    cc->setDrawName("Cricle");
    cc->setHeight(80);
    cc->setWidth(80);
    drawManager.addDrawing("Circle", cc);
    
    //梯形
    DrawPrototype* tz = new ConcreteDrawing();
    tz->setDrawName("Trapezoidal");
    tz->setHeight(50);
    tz->setWidth(50);
    drawManager.addDrawing("Trapezoidal", tz); 

    //直线
    DrawPrototype* ln = new ConcreteDrawing();
    ln->setDrawName("Line");
    ln->setHeight(100);
    ln->setWidth(1);
    drawManager.addDrawing("Line", ln); 

    //调用原型的Clone方法获取浅拷贝对象
    //绘制(拖出)第1个矩形
    ConcreteDrawing* rect1 = (ConcreteDrawing*)drawManager.getDrawing("Rectangle")->clone(); 
    rect1->setHeight(197);
    rect1->showInfo();  
    
    //绘制(拖出)第2个矩形
    ConcreteDrawing* rect2 = (ConcreteDrawing*)drawManager.getDrawing("Rectangle")->clone(); 
    rect2->setWidth(112);
    rect2->showInfo();  

    //绘制(拖出)第3个矩形(默认大小)
    ConcreteDrawing* rect3 = (ConcreteDrawing*)drawManager.getDrawing("Rectangle")->clone(); 
    rect3->showInfo(); 

    //画线
    ConcreteDrawing* line = (ConcreteDrawing*)drawManager.getDrawing("Line")->clone(); 
    line->showInfo();         
    
    return 0;
}

posted on 2018-04-09 20:25  arabain  阅读(85)  评论(0)    收藏  举报

导航