什么是原型模式?

在GOF的《设计模式:可复用面向对象软件的基础》中是这样说的:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。这这个定义中,最重要的一个词是“拷贝”,也就是口头上的复制,而这个拷贝,也就是原型模式的精髓所在。

举一个最简单的例子来说明原型模式:记得上小学的时候,老师把需要做的课外习题写到黑板上,而下面的我们都要把这些题抄写到自己的本子上,回家做好,第二天交上来,也就是每道题,全班50个人,每个人都要抄写一遍。按照现在的时间理论来说,就是浪费了50个人的时间。但是,那个时候条件限制,老师也是不得已而为之。现在好了,老师做一份电子版的习题,打印一份,然后拿着这份打印的原版,就可以复制出50份。

结合原型模式的概念进行分析,老师打印出来的那一份,就是“原型”,而复制出来的那50份,就是使用的“拷贝”。而原型模式就是这么简单的一个道理,通过现有的东西,再复制出一个来。

为什么要使用原型模式?

原型模式和建造者模式、工厂方法模式一样,都属于创建型模式的一种。简单的来说,我们使用原型模式,就是为了创建对象。但是,在以下场景下,使用原型模式是最好的选择:

  1. 当我们的对象类型不是开始就能确定的,而这个类型是在运行期确定的话,那么我们通过这个类型的对象克隆出一个新的对象比较容易一些;
  2. 有的时候,我们需要一个对象在某个状态下的副本,此时,我们使用原型模式是最好的选择;例如:一个对象,经过一段处理之后,其内部的状态发生了变化;这个时候,我们需要一个这个状态的副本,如果直接new一个新的对象的话,但是它的状态是不对的,此时,可以使用原型模式,将原来的对象拷贝一个出来,这个对象就和之前的对象是完全一致的了;
  3. 当我们处理一些比较简单的对象时,并且对象之间的区别很小,可能就几个属性不同而已,那么就可以使用原型模式来完成,省去了创建对象时的麻烦了;
  4. 有的时候,创建对象时,构造函数的参数很多,而自己又不完全的知道每个参数的意义,就可以使用原型模式来创建一个新的对象,不必去理会创建的过程,让创建过程见鬼去吧。

所以,在上述的的情况下,在设计的时候,适当的考虑一下原型模式,减少对应的工作量,减少程序的复杂度,提高效率。

用UML类图表示原型模式

由于克隆需要一个原型,而上面的类图中Prototype就这个原型,Prototype定义了克隆自身的Clone接口,由派生类进行实现,而实现原型模式的重点就在于这个Clone接口的实现。ConcretePrototype1类和ConcretePrototype2类继承自Prototype类,并实现Clone接口,实现克隆自身的操作;同时,在ConcretePrototype1类和ConcretePrototype2类中需要重写默认的复制构造函数,供Clone函数调用,Clone就是通过在内部调用重写的复制构造函数实现的。在后续的编码过程中,如果某个类需要实现Clone功能,就只需要继承Prototype类,然后重写自己的默认复制构造函数就好了。好比在C#中就提供了ICloneable接口,当某个类需要实现原型模式时,只需要实现这个接口的道理是一样的。

代码实现

 #include <iostream>
using namespace std; //接口
class Prototype
{
public :
Prototype(){}
virtual ~Prototype(){} virtual Prototype * Clone() = ;
}; //实现
class ConcretePrototype : public Prototype
{
public :
ConcretePrototype():m_counter(){}
virtual ~ConcretePrototype(){} //拷贝构造函数
ConcretePrototype( const ConcretePrototype & rhs)
{
m_counter = rhs .m_counter;
} //复制自身
virtual ConcretePrototype * Clone()
{
//调用拷贝构造函数
return new ConcretePrototype (*this );
} private :
int m_counter;
}; int main(int argc , char **argv)
{
//生成对像
ConcretePrototype * conProA = new ConcretePrototype (); //复制自身
ConcretePrototype * conProB = conProA->Clone(); delete conProA;
conProA= NULL ; delete conProB;
conProB= NULL ; return ;
}

上述代码实现了一个最简单的原型模式,但是已经将原型模式的基本实现原理展现出来了。而有的时候,当调用Clone获得了一个复制的对象以后,需要改变对象的状态,此时就可能需要在ConcretePrototype类中添加一个Initialize操作,专门用于初始化克隆对象。由于在Clone的内部调用的是复制构造函数,而此处又涉及到深复制和浅复制的问题。所以,在实际操作的过程中,这些问题,都需要进行仔细的考虑。

与其它创建型模式的比较

工厂方法模式、抽象工厂模式、建造者模式和原型模式都是创建型模式。工厂方法模式适用于生产较复杂,一个工厂生产单一的一种产品的时候;抽象工厂模式适用于一个工厂生产多个相互依赖的产品;建造者模式着重于复杂对象的一步一步创建,组装产品的过程,并在创建的过程中,可以控制每一个简单对象的创建;原型模式则更强调的是从自身复制自己,创建要给和自己一模一样的对象。

总结

原型模式作为创建型模式中最特殊的一个模式,具体的创建过程,是由对象本身提供,这样我们在很多的场景下可以很方便的快速的构建新的对象。但是,原型模式的最大缺点是继承原型的子类都要实现Clone操作,这个是很困难的。例如,当所考虑的类已经存在时就难以新增Clone操作。当内部包括一些不支持拷贝或者有循环引用的对象时,实现克隆可能也会很困难。说以说,每一种设计模式都有它的优点和缺点,在设计的时候,我们需要进行权衡各方面的因素,扬长避短。

C++设计模式——原型模式的更多相关文章

  1. 10. 星际争霸之php设计模式--原型模式

    题记==============================================================================本php设计模式专辑来源于博客(jymo ...

  2. PHP 设计模式 原型模式(Prototype)之深/浅拷贝

      看PHP 设计模式 原型模式(Prototype)时,衍生出一个扩展问题之 原型拷贝的浅拷贝和深拷贝问题(不管写Java还是写PHP还是写JS时都多多少少遇到过对象拷贝问题)   比如写前端页面时 ...

  3. PHP设计模式 原型模式(Prototype)

    定义 和工厂模式类似,用来创建对象.但实现机制不同,原型模式是先创建一个对象,采用clone的方式进行新对象的创建. 场景 大对象的创建. 优点 1.可以在运行时刻增加和删除产品 2.可以改变值或结构 ...

  4. 【设计模式】Java设计模式 - 原型模式

    [设计模式]Java设计模式 - 原型模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起 ...

  5. C#设计模式-原型模式

    在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,如果我们用new操作符去创建这样的类实例,这未免会增加创建类的复杂度和耗费更多的内存空间,因为这样在内存中分配 ...

  6. [工作中的设计模式]原型模式prototype

    一.模式解析 提起prototype,最近看多了js相关的内容,第一印象首先是js的原型 var Person=function(name){ this.name=name; } Person.pro ...

  7. 我的Java设计模式-原型模式

    "不好意思,我是卧底!哇哈哈哈~"额......自从写了上一篇的观察者模式,就一直沉浸在这个角色当中,无法自拨.昨晚在看<使徒行者2>,有一集说到啊炮仗哥印钞票,我去, ...

  8. Java设计模式—原型模式

    原型设计模式是一种比较简单的设计模式,在项目中使用的场景非常多. 个人理解: 原型模式实现了对Java中某个对象的克隆功能,即该对象的类必须implements实现Cloneable接口来标识为可被克 ...

  9. 设计模式——原型模式(Prototype)

    用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象.——DP UML类图 模式说明 如果把在一张纸上手写一篇简历的过程看成是类的实例化过程,那么通过原型模式创建对象的过程就是拿着这张纸到复印 ...

随机推荐

  1. C# PDF转Image图片

    概述 PDF是常用的文件格式之一,通常情况下,我们可以使用itextsharp生产PDF文件:可是如何将PDF文件转换成图片那?目前常用的: 思路1.根据PDF绘画轨迹重新绘制图片: 思路2.是将PD ...

  2. Please check that your locale settings问题

    问题描述: perl: warning: Setting locale failed. perl: warning: Please check that your locale settings: L ...

  3. 轻松测试 logstash 的配置文件

    配置文件本身非常脆弱!所以修改配置文件自然会引入部署失败的风险.如果能够对配置文件进行自动化测试将会极大的降低这种风险.本文将介绍一个可以自动化测试 logstash 配置文件的工具,让大家可以像写单 ...

  4. java中内存分配

    java程序运行时内存分配详解  一. 基本概念 每运行一个java程序会产生一个java进程,每个java进程可能包含一个或者多个线程,每一个Java进程对应唯一一个JVM实例,每一个JVM实例唯一 ...

  5. iso data 聚类算法

    isodata算法就是先拟定一个预期类,再选取一些聚类中心,通过不断合并或者分裂聚类,达到分类的目的 关键就是在于,如何分裂,合并 要合并或者分裂 必须要确定一些指标 所以第一步就是要确定 某些指标 ...

  6. MYSQL 两表 排除 重复记录

    MySQL之——查询重复记录.删除重复记录方法大全 - 冰河的专栏 - CSDN博客https://blog.csdn.net/l1028386804/article/details/51733585 ...

  7. VUE实例方法添加、COOKIE、Token

    创建COOKIE const COOKIE = { KEY_TOKEN: 'access_token', KEY_USER: 'nbuser', KEY_PERMISSION: 'nb-permiss ...

  8. 【XSY3042】石像 拓扑排序 状压DP 洲阁筛

    题目大意 有 \(n\) 个整数 \(a_1,a_2,\ldots,a_n\),每个数的范围是 \([1,m]\).还有 \(k\) 个限制,每个限制 \(x_i,y_i\) 表示 \(a_{x_i} ...

  9. Python 面向对象高阶-----metaclass

    Python 面向对象高阶-----metaclass 前言 类也是对象,既然类是对象,那就自然是某个东西的实例化,这个东西就是type 首先看下type是怎么回事 type type最常用的方法就是 ...

  10. 2017-12-19python全栈9期第四天第二节之列表的增删查改之按索引改和按切片改

    #!/user/bin/python# -*- coding:utf-8 -*-li = ['zs','ls','ww','zl','xx']li[0] = 'cxvb' #按索引位置print(li ...