相信大多数的人都看过《西游记》,对孙悟空拔毛变出小猴子的故事情节应该都很熟悉。孙悟空可以用猴毛根据自己的形象复制出很多跟自己一模一样的小猴兵出来,其实在设计模式中也有一个类似的模式,我们可以通过一个原型对象来克隆出多个一模一样的对象,这个模式就是原型模式。

一 大同小异的工作周报

  M公司一直在使用自行开发的一个OA系统进行日常工作办理,但在使用过程中,越来越多的人对工作周报的创建和编写模块产生了抱怨。追其原因,M公司的OA管理员发现,由于某些岗位每周工作存在重复性,工作周报内容都大同小异,如下图所示:

  这些周报只有一些小地方存在差异,但是现行系统每周默认创建的周报都是空白报表,因此用户只能通过重新输入或不断地复制与粘贴来填写重复的周报内容,极大地降低了工作效率,浪费宝贵的时间。如何快速创建相同或者相似的工作周报,成为了M公司软件开发人员的一个新问题。

  M公司开发人员经过分析,决定按照以下思路对工作周报模块进行重新设计:

  (1)除了允许用户创建新周报外,还允许用户将创建好的周报保存为模板(也就是原型)。

  (2)用户在再次创建周报时,可以创建全新的周报,还可以选择合适的模板复制生成一个相同的周报,然后对新生成的周报根据实际情况进行修改,产生新的周报。

二 原型模式概述

2.1 关于原型模式

  原型模式的原理很简单,将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象克隆自己来实现创建过程。

原型模式(Prototype):使用原型实例指定创建对象的种类,并且通过拷贝这些原 型创建新的对象。原型模式是一种对象创建型模式。

  需要注意的是,通过克隆方法所创建的对象时全新的对象。

2.2 类图

  

2.3 代码实现

2.3.1 抽象克隆类

class AbstractClone
{
protected:
AbstractClone()
{
//cout << "AbstractClone Construct" << endl;
} public:
~AbstractClone()
{
//cout << "AbstractClone Deconstruct" << endl;
} virtual AbstractClone* Clone() = ;
virtual void PrintWeekly() = ;
};

2.3.2 周报类

class CWeeklyClone : public AbstractClone
{
public:
/*CWeeklyClone()
{
cout << "CWeeklyLogClone Construct" << endl;
}*/ CWeeklyClone(string strName="", string strDate="", string strContent="", CAttachment *pAttachment=NULL)
{
//cout << "CWeeklyLogClone Construct" << endl; m_strName = strName;
m_strDate = strDate;
m_strContent = strContent;
if (pAttachment!= NULL)
{
m_pAttachment = new CAttachment(*pAttachment);
}
else
{
m_pAttachment = NULL;
} }
CWeeklyClone(CWeeklyClone& other)
{
// 拷贝构造函数需要注意深拷贝和浅拷贝的问题
m_strName = other.m_strName;
m_strDate = other.m_strDate;
m_strContent = other.m_strContent; if (other.m_pAttachment != NULL)
{
// 浅拷贝方式
// m_pAttachment = other.m_pAttachment;
// 深拷贝方式
m_pAttachment = new CAttachment(*other.m_pAttachment); }
else
{
m_pAttachment = NULL;
}
//cout << "CWeeklyClone CopyConstruct" << endl;
} ~CWeeklyClone()
{
if (m_pAttachment != NULL)
{
delete m_pAttachment;
m_pAttachment = NULL;
}
//cout << "CWeeklyLogClone Deonstruct" << endl;
} CWeeklyClone* Clone()
{
return new CWeeklyClone(*this);
} void SetName(string strName)
{
m_strName = strName;
} void SetDate(string strDate)
{
m_strDate = strDate;
}
void SetContent(string strContent)
{
m_strContent = strContent;
}
void SetAttachment(CAttachment *pAttachment)
{
if (m_pAttachment != NULL)
{
m_pAttachment = pAttachment;
}
}
CAttachment *GetAttachment()
{
return m_pAttachment;
}
void PrintWeekly()
{
cout << "start:------------M公司个人工作周报------------" << endl;
cout << "周次:" << m_strDate <<endl;
cout << "员工:" << m_strName <<endl;
cout << "内容:" << m_strContent << endl;
if (m_pAttachment != NULL)
cout << "附件:" << m_pAttachment->m_strContent << endl; cout << "end:------------M公司个人工作周报------------" << endl;
}
private:
string m_strName;
string m_strDate;
string m_strContent; CAttachment *m_pAttachment;
};

2.3.3 附件类

#include <string>
#include <iostream>
using namespace std; // 附件类
class CAttachment
{
public:
CAttachment(string strContent="")
{
m_strContent = strContent;
//cout << "CAttachment CopyConstruct" << endl;
}
CAttachment(CAttachment &other)
{
m_strContent = other.m_strContent;
//cout << "CAttachment CopyConstruct" << endl;
}
~CAttachment()
{
//cout << "CAttachment Deonstruct" << endl;
} public:
string m_strContent;
};

2.4 测试

#include "stdio.h"

#include "prototype.h"

void main()
{
// 创建附件
CAttachment *pAttachment = new CAttachment("附件:紫贝龙二号文件"); // 创建周报
CWeeklyClone *pWeekly1 = new CWeeklyClone("张林", "第一周", "ddcpy模块单元测试", pAttachment);
pWeekly1->PrintWeekly(); CWeeklyClone *pWeekly2 = pWeekly1->Clone();
pWeekly2->SetDate("第二周");
pWeekly2->PrintWeekly(); // 判断周报是否相同
cout << "深拷贝模式下判断周报和附件是否相同:" << endl;
pWeekly1==pWeekly2 ? (cout << "周报相同"<< endl):(cout << "周报不相同" << endl);
pWeekly1->GetAttachment()==pWeekly2->GetAttachment() ? (cout << "附件相同"<< endl):(cout << "附件不相同" << endl);
return;
}

三 原型模式总结

3.1 主要优点

  (1)当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有的实例可以提高新实例的创建效率。

  (2)可以使用深复制的方式保存对象的状态。将对象复制一份并将其状态保存起来,以便于在使用的时候使用,比如恢复到某一个历史状态,可以辅助实现撤销操作。

3.2 主要缺点

  (1)需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了开闭原则

  (2)为了支持深复制,当对象之间存在多重嵌套引用关系时,每一层对象都必须支持深复制,实现起来可能比较麻烦。

3.3 应用场景

  最主要的应用场景就在于 创建新对象成本较大(例如初始化需要占用较长的时间,占用太多的CPU资源或者网络资源),新的对象可以通过原型模式对已有对象进行复制来获得。如果是相似对象,则可以对其成员变量稍作修改。

设计模式之原型(prototype)模式的更多相关文章

  1. 设计模式C++描述----08.原型(Prototype)模式

    一. 概述 定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 换句话说,就是不用重新初始化对象,而是动态地获得对象运行时的状态. 再说明白点,就是要一个拷贝过构造函数类似功能的接 ...

  2. Java 实现原型(Prototype)模式

    public class BaseSpoon implements Cloneable {//spoon 匙, 调羹 String name; public String getName() { re ...

  3. 原型(Prototype)模式

    原型模式属于对象的创建模式.通过给出一个原型对象来指明所有创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象.这就是原型模式的用意.原型模式的结构 原型模式要求对象实现一个可以“克隆 ...

  4. 设计模式--原型(Prototype)模式

    写这些也许有人认为“为了模式而模式”.Insus.NET所想到的,每个大师成为大师之前,也许都得这样做. 走路,从小就开始学,直至现在,谁还不是为了走路而走路?一直重复着...... 很多人没有分享自 ...

  5. 六、原型(Prototype)模式

    原型模式是对象的创建模式,通过给出一个原型对象来指明所要创建的对象的类型.然后用复制这个原型对象的方法来创建出更多同类型的对象. 原型模式可以不用重新初始化对象,而动态的获取对象运行时的状态.使用原型 ...

  6. 克隆复制可使用原型( Prototype)设计模式

    今天有学习设计模式的原型(Prototype)<设计模式--原型(Prototype)模式>http://www.cnblogs.com/insus/p/4152773.html .为了加 ...

  7. 设计模式_11_原型模式(prototype)深拷贝、浅拷贝

    设计模式_11_原型模式(prototype) 浅拷贝: package designPatternOf23; /** * 定义:用原型实例,指定创建对象的种类,并通过拷贝这些原型创建新的对象 * P ...

  8. [设计模式] 4 原型模式 prototype

    设计模式:可复用面向对象软件的基础>(DP)本文介绍原型模式和模板方法模式的实现.首先介绍原型模式,然后引出模板方法模式. DP书上的定义为:用原型实例指定创建对象的种类,并且通过拷贝这些原型创 ...

  9. 乐在其中设计模式(C#) - 原型模式(Prototype Pattern)

    原文:乐在其中设计模式(C#) - 原型模式(Prototype Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 原型模式(Prototype Pattern) 作者:weba ...

  10. C#设计模式之六原型模式(Prototype)【创建型】

    一.引言 在开始今天的文章之前先说明一点,欢迎大家来指正.很多人说原型设计模式会节省机器内存,他们说是拷贝出来的对象,这些对象其实都是原型的复制,不会使用内存.我认为这是不对的,因为拷贝出来的每一个对 ...

随机推荐

  1. Java 基础总结(二)

    本文参见:http://www.cnblogs.com/dolphin0520/category/361055.html 1. 字节流与和字符流 1). 字符流操作时使用了缓冲区,而在关闭字符流时会强 ...

  2. 浏览器 Event对象 及 属性 的兼容处理

    摘自: http://blog.csdn.net/jiachunfeng/article/details/6448186 event对象 IE 中可以直接使用 event 对象,而 FF 中则不可以, ...

  3. mac相关记录

    一.设置允许安装任何来源软件 命令行执行: sudo spctl --master-disable -----------------------------

  4. 负载均衡集群ipvsadm命令及基本用法

    ipvsadm是LVS在应用层的管理命令,我们可以通过这个命令去管理LVS的配置.需要使用yum单独安装. 基本用法: ipvsadm COMMAND [protocol] service-addre ...

  5. python步长为负时的情况

    Sequence[start:end:step] python 的序列切片中,第一个:隔离了 起始索引 和 结束索引,第二个:隔离了 结束索引和 步长 step为正,则从左到右切片,如果 start ...

  6. 小技巧|使用Vue.js的Mixins复用你的代码

    Vue中的混入 mixins 是一种提供分发 Vue 组件中可复用功能的非常灵活的方式.听说在3.0版本中可能会用Hooks的形式实现,但这并不妨碍它的强大. 这里主要来讨论 mixins 如何优化我 ...

  7. 外部类与main方法笔记

    外部类 1. 外部public class只能有一个 2. 外部类只能有两种访问控制级别: public 和默认 3. 一个文件中,可以有多个public class,即外部类为public,还可以有 ...

  8. Mongo, Express, Angular, Node-- MEAN Stack搭建

    前言 作为一个从后端转全栈的码农,我一直使用express,jade & bootstrap, jquery的组合.重复了几次相同的工作后,看到网上开始流行MEAN Stack,于是也对其研究 ...

  9. mssqlserver,mysql,oracle分页查询

    分页查询语句是sql语句编程中很长见的一个典型应用,用sql语句来分页比一些分页控件的速度要快,所以sql语句的分页在实际编程应用中还是非常广泛的. 今天给大家分享几条不同数据库编程用的分页查询语句. ...

  10. js插件封装

    插件封装原则 1.暴露出来的实例必须只能是一个 2.IIFE包裹 !执行包裹 函数作用域保护 3.实例化方法不要写在函数内 throw这个方法是报错