设计模式系列之原型模式

prototype模式通过实例对象指定需要创建的类型,这与上一篇我们提到的factory method模式有本质不同,factory method模式是通过类的继承定义不同子类来达到创建不同类型对象的目的,属于类模式,prototype模式通过调用组合的对象成员生成不同类型的对象实例,属于对象模式。

由于这个特性,prototype具有以下适用场合:

· 需要运行时确定实例化的类时,比如动态装载库时

· 避免创建过多子类时。子类太多永远是不受欢迎的,在factory method中我们也提到通过模板或者参数化来减少子类数目。

· 实例化对象的状态组合过多时,先建立原型库,使用时通过注册表取得相应原型来生成目标对象会更加方便。

prototype的优点包括:

· 运行时增加和删除产品

· 改变对象,这点是组合模式的共同优点

· 减少子类的数目

· 用类动态配置应用

prototype的最大缺点应该就是在某些语言中实现clone操作可能会很困难,特别是包含循环引用的额情况下。

下面继续使用上一篇文章中的例子,开发一个芯片设计软件,用prototype实现。所有的图形继承自MaskFigure,必须实现clone函数,MaskDesigner初始化时传入对象原型指针,MakeFigure时通过原型的clone生成新的图形。Factory method是通过图形对应的类调用图形类的构造函数生成新的对象,通俗点讲,就好像factory method是用设计图纸重新画了个图形出来,而prototype是临摹已经画好了的图形。类结构如下:

代码实现如下:

//mask.hpp
#ifndef MASK_HPP
#define MASK_HPP

class MaskFigure{
  public:
    virtual ~MaskFigure()=0;
    virtual MaskFigure* clone()=0;
  protected:
    MaskFigure();
    MaskFigure(const MaskFigure&);
};

class MaskRound:public MaskFigure {
  public:
    MaskRound();
    MaskRound(const MaskRound&);
    MaskRound* clone();
    ~MaskRound();
};

class MaskRec:public MaskFigure {
  public:
    MaskRec();
    MaskRec(const MaskRec&);
    MaskRec* clone();
    ~MaskRec();
};

class MaskTri:public MaskFigure {
  public:
    MaskTri();
    MaskTri(const MaskTri&);
    MaskTri* clone();
    ~MaskTri();
};
#endif
//mask.cpp
#include <iostream>
#include "mask.hpp"

using std::cout;
using std::endl;

MaskFigure::MaskFigure() {
  cout<<"init MaskFigure"<<endl;
}

MaskFigure::MaskFigure(const MaskFigure& mf) {
  cout<<"copy Figure"<<endl;
}

MaskFigure::~MaskFigure() {
  cout<<"delete MaskFigure"<<endl;
}

MaskRound::MaskRound() {
  cout<<"Draw roundness on Mask"<<endl;
}

MaskRound::MaskRound(const MaskRound& mr) :MaskFigure(mr){
  cout<<"copy roundness"<<endl;
}

MaskRound* MaskRound::clone() {
  return new MaskRound(*this);
}

MaskRound::~MaskRound() {
  cout<<"delete MaskRound"<<endl;
}

MaskRec::MaskRec() {
  cout<<"Draw rectangle on Mask"<<endl;
}

MaskRec::MaskRec(const MaskRec& mr) :MaskFigure(mr){
  cout<<"copy rectangle"<<endl;
}

MaskRec* MaskRec::clone() {
  return new MaskRec(*this);
}

MaskRec::~MaskRec() {
  cout<<"delete MaskRec"<<endl;
}

MaskTri::MaskTri() {
  cout<<"Draw triangle on Mask"<<endl;
}

MaskTri::MaskTri(const MaskTri& mt) :MaskFigure(mt){
  cout<<"copy triangle"<<endl;
}

MaskTri* MaskTri::clone() {
  return new MaskTri(*this);
}

MaskTri::~MaskTri() {
  cout<<"delete MaskTri"<<endl;
}

//maskdesigner.hpp
#ifndef FIGUREDESIGNER_HPP
#define FIGUREDESIGNER_HPP

#include "mask.hpp"

class FigureDesigner{
 public:
  FigureDesigner(MaskFigure *mf){
    figure = mf;
  }

  MaskFigure* MakeFigure(){
    return figure->clone();
  }

 private:
  MaskFigure *figure;
};
#endif

//main.cc
#include <memory>
#include <iostream>
#include "maskdesigner.hpp"

using std::cout;
using std::endl;
using std::shared_ptr;

int main() {
  MaskRound   mro;
  MaskRec     mre;
  MaskTri     mtr;
  FigureDesigner  md1(&mro);
  shared_ptr<MaskFigure> mfptr1(md1.MakeFigure());
  FigureDesigner  md2(&mre);
  shared_ptr<MaskFigure> mfptr2(md2.MakeFigure());
  FigureDesigner  md3(&mtr);
  shared_ptr<MaskFigure> mfptr3(md3.MakeFigure());
}

讲到prototype模式,有必要提一下c++ Covariance特性,即c++的协变性质,在虚函数实现时,一般要求派生类重写的虚函数必须与基类函数有一致的返回值类型,参数个数及类型。

不过这也有个特例,如果返回值类型满足下面条件时可以不同,

1. 基类和派生类虚函数返回值类型都是指向类的指针或者引用

2. 基类虚函数返回值指针或引用所指向的类类型是派生类返回值指针或引用所指向的类类型的间接或直接基类,即属于同一个继承体系。

这表示如下情况是允许的,

class Base { /* ... */ };
class Derived: public Base { /* ... */ };

class B {
  virtual Base* func() { return new Base; }
  virtual ~B() { }
};
class D: public B {
  Derived* func() { return new Derived; }
virtual ~D() { }
};

fanc的返回值分别是base*和Derived*,属于同一继承体系内。但返回值换成其他类型如基本类型时则不允许。这一特性在prototype中得到充分的应用, MaskFigure的clone函数返回 MaskFigure*指针类型,MaskRound,MaskRec和MaskTri的clone函数分别返回MaskRound*,MaskRec*和MaskTri*指针类型。

(完)

时间: 2024-11-05 20:38:11

设计模式系列之原型模式的相关文章

设计模式学习05—原型模式

一.动机与定义 之前学习原型模式一直以为原型模式目的是为了方便的创建相同或相似对象,用复制对象的方式替换new的方式,还研究了深克隆和浅克隆.最近仔细看了GOF的设计模式,发现原型模式的本意并不仅仅是复制对象这么简单. 复制对象确实是一方面,当我们需要大量相似,甚至相同对象的时候,除了一个个的new之外,还可以根据一个原型,直接复制出更多的对象.但是如果把原型模式认为只是复制对象这么简单就错了. 创建型模式主要讲如何创建对象,通常包含何时创建,谁来创建,怎么创建等.GOF书里面写的意图是,用原型

Java描述设计模式(05):原型模式

一.原型模式简介 1.基础概念 原型模式属于对象的创建模式.通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象. 2.模式结构 原型模式要求对象实现一个可以"克隆"自身的接口,这样就可以通过复制一个实例对象本身来创建一个新的实例.这样一来,通过原型实例创建新的对象,就不再需要关心这个实例本身的类型,只要实现了克隆自身的方法,就可以通过这个方法来获取新的对象,而无须再去通过new来创建. 3.代码实现 1).UML关系图 Java描述设计模

[js高手之路]设计模式系列课程-组合模式+寄生组合继承实战新闻列表

所谓组合模式,就是把一堆结构分解出来,组成在一起,现实中很多这样的例子,如: 1.肯德基套餐就是一种组合模式, 比如鸡腿堡套餐,一般是是由一个鸡腿堡,一包薯条,一杯可乐等组成的 2.组装的台式机同理,由主板,电源,内存条,显卡, 机箱,显示器,外设等组成的 把一个成型的产品组成部件,分成一个个独立的部件,这种方式可以做出很多灵活的产品,这就是组合模式的优势 比如:家用台式机电脑,要求配置比较低, 这个时候只需要主板+电源+内存条+机箱+显示器+外设就可以了,不需要配置独立显卡 鸡腿堡+鸡翅+紫薯

php设计模式 四 (观察者 原型模式 迭代器模式)

观察者模式 观察者模式(有时又被称为发布-订阅Subscribe>模式.模型-视图View>模式.源-收听者Listener>模式或从属者模式)是软件设计模式的一种.在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知.这通常透过呼叫各观察者所提供的方法来实现.此种模式通常被用来实现事件处理系统. 当一个对象的状态发生改变时,依赖他的对象全部会接到通知,并自动更新.观察者模式实现了低耦合 非入侵式的通知与更新机制. 观察者模式示例: 首先创建一个事

Android设计模式系列--工厂方法模式

工厂方法模式,往往是设计模式初学者入门的模式,的确,有人称之为最为典型最具启发效果的模式.android中用到了太多的工厂类,其中有用工厂方法模式的,当然也有很多工厂并不是使用工厂方法模式的,只是工具管理类.今天以ThreadFactory举例说明一下简单工厂模式和工厂方法模式. 工厂方法模式,Factory Method,简单的方式,不简单的应用. 1.意图定义一个用于创建对象的接口,让子类决定实例化哪个类.工厂方式模式使一个类的实例化延迟到其子类.热门词汇:虚构造器 延迟 创建对象 子类 2

《Java设计模式》之原型模式

原型模式属于对象的创建模式.通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象.这就是原型模式的用意. 原型模式的结构 原型模式要求对象实现一个可以"克隆"自身的接口,这样就可以通过复制一个实例对象本身来创建一个新的实例.这样一来,通过原型实例创建新的对象,就不再需要关心这个实例本身的类型,只要实现了克隆自身的方法,就可以通过这个方法来获取新的对象,而无须再去通过new来创建. 原型模式有两种表现形式:(1)简单形式.(2)登记形式,这两

从王者荣耀看设计模式(十七.原型模式)

从王者荣耀看设计模式(原型模式) 一.简介 王者荣耀包含有很多的玩法,其中有一种游戏模式只在周六和周日开放,那就是--克隆模式.与常规的游戏双方阵营只允许出现单一英雄不同,在克隆模式中,双方各选择一个英雄进行克隆,换句话说,一个阵营的五个人操作的五个相同的英雄 二.模式动机 在软件系统中,有些对象的创建过程比较复杂,而且有时候需要频繁创建,原型模式通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象,这就是原型模式的意图所在. 三.原型模式 原型模式

设计模式系列 - 创建型模式

单例模式 懒汉式,线程不安全. 除非是单线程程序,否则不推荐使用. public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } 饿汉式,线程安全 当程序总是使用这个

java设计模式 GOF23 04 原型模式

一.原型模式介绍 因为java中new一个新对象比clone一个对象需要花费等多的资源,所以一般需要 在短时间内创建大量对象并且new对象的过程需要耗费比较多的资源使用原型模式. 想要clone一个类需要这个类实现Cloneable接口,重载clone方法,这个接口在底层 通过内存拷贝实现clone对象,因此效率很高. package com.lz.prototype; import java.util.Date; public class ShallowClone implements Clo