用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。——DP

UML类图

模式说明

如果把在一张纸上手写一篇简历的过程看成是类的实例化过程,那么通过原型模式创建对象的过程就是拿着这张纸到复印机上复印的过程。即原型模式中的一个对象可以自我克隆生成另一个对象,而无需再次通过类的初始化过程。由于克隆操作比较常见,.NET已经提供了ICloneable接口(包含唯一一个方法Clone),所以在.NET中,只要实现这个接口,就算实现原型模式了。

下面的示例中,定义了两个类,一个简历类和一个地址类,并且两个类都实现ICloneable接口。

    /// <summary>
/// 在.Net中提供了ICloneable接口,无需再定义一个父类
/// </summary>
class Resume : ICloneable
{
public string Name { get; set; } public int Age { get; set; } public Address Addr { get; set; } public string Remark { get; set; } public Resume(string name)
{
Addr = new Address();
this.Name = name;
} // 原型模式
public object Clone()
{
Resume resume = this.MemberwiseClone() as Resume; //.NET中可以用MemberwiseClone方法创建自身的浅表副本
resume.Addr = this.Addr.Clone() as Address; return resume;
} public override string ToString()
{
return string.Format("姓名:{0},年龄:{1},地址:{2}——{3}", Name, Age, Addr.City + Addr.Street, Remark);
}
} class Address : ICloneable
{
public string City { get; set; } public string Street { get; set; } //原型模式
public object Clone()
{
return this.MemberwiseClone();
}
}

对于原型模式的实现,要注意一点,就是复制的深度问题。由于MemberwiseClone方法,实现的是浅拷贝,即被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。这样如果Resume类的Clone方法直接返回this. MemberwiseClone(),那么新对象和原对象的Addr属性将引用相同的Address对象,这里对this. MemberwiseClone()返回的浅拷贝对象的Addr属性,再次进行一次克隆,以便对Resume对象的Clone操作后,能生成一个完全独立的对象。

这样在客户端调用时,就完全可以通过对象的克隆来创建更多的新对象:

        static void Main(string[] args)
{
/*
* 原型模式
* 复制简历
*
*
* 原型模式的实质是对于构造非常复杂的情况,比如在构造函数的时候
* 需要远程读取信息或者要完成许多复杂操作的时候 这时候用clone就会非常方便地获取其他实例的状态
*
*
* 用了DataTable的Clone方法就知道了,为什么DataTable要这个方法,原因是当得到一个DataTable对象的时候
* 又想创建一个一样的DataTable的话,如果没有Clone,那么就要创建Columns,这个是很麻烦的
* 但是有了Clone就很爽了,直接调用就行
*
*/ Resume resume1 = new Resume("我的简历模板");
resume1.Age = ; //梦想着还是18岁……
resume1.Addr.City = "湖州市";
resume1.Addr.Street = "二环西路";
resume1.Remark = "这份简历是自己留着,作为复印模板的。"; Resume resume2 = (Resume)resume1.Clone(); //对象克隆
resume2.Name = "苦哥";
resume2.Age = ;
resume2.Remark = "这是给软件公司的简历"; Resume resume3 = (Resume)resume2.Clone(); //对象克隆
resume3.Name = "苦哥大人";
resume3.Addr.City = "合肥市";
resume3.Addr.Street = "长江路";
resume3.Remark = "这是投到合肥的简历,写成住合肥,是不是会亲近点?"; Console.WriteLine(resume1.ToString());
Console.WriteLine(resume2.ToString());
Console.WriteLine(resume3.ToString()); Console.Read();
}

总结

原型模式的实现过程中,要知道两个概念:

  • 浅复制:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。
  • 深复制:把引用对象的变量指向复制过来的新对象,而不是原有的被引用的对象。

还有一点要考虑到的就是深复制的深度问题,如果对象引用的对象内,又包含了对其他对象的引用……这样在深复制时要考虑复制多少层的问题,对于这种情况,可以通过序列化与反序列化的方式,来达到对象深复制的目的。

参考

  1. 程杰老师  《大话设计模式》

设计模式——原型模式(Prototype)的更多相关文章

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

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

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

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

  3. C#设计模式——原型模式(Prototype Pattern)

    一.概述 在软件开发中,经常会碰上某些对象,其创建的过程比较复杂,而且随着需求的变化,其创建过程也会发生剧烈的变化,但他们的接口却能比较稳定.对这类对象的创建,我们应该遵循依赖倒置原则,即抽象不应该依 ...

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

    场景分析: 前面我们提到,交易对象Trade,还有继承他的债券交易BondTrade.期货交易FutureTrade. 现在有一个需求,需要提供方法将交易拆分成多笔小交易. 代码如下(如果没有clon ...

  5. 设计模式——原型模式(Prototype Pattern)

    原型模式:用原型实例制定创建对象的种类,并且通过拷贝这些原型创建新的对象. UML 图: 原型类: package com.cnblog.clarck; /** * 原型类 * * @author c ...

  6. 大话设计模式--原型模式 Prototype -- C++实现

    1. 原型模式: 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象... 注意: 拷贝的时候是浅拷贝 还是 深拷贝, 来考虑是否需要重写拷贝构造函数. 关键在于: virtual Pro ...

  7. 设计模式--原型模式Prototype(创建型)

    一.原型模式 用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象.原型模式实现的关键就是实现Clone函数,还需要实现深拷贝. 二.UML类图 三.例子 //父类 class Resume ...

  8. 谈谈设计模式~原型模式(Prototype)

    返回目录 原型模式是创建型模式的一种,其特点在于通过“复制”一个已经存在的实例来返回新的实例(clone),而不是新建(new)实例.被复制的实例就是我们所称的“原型”,这个原型是可定制的. 原型模式 ...

  9. Net设计模式实例之原型模式( Prototype Pattern)

    一.原型模式简介(Brief Introduction) 原型模式(Prototype Pattern):用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象. Specify the kin ...

随机推荐

  1. VC6++常用快捷键

    VC6快捷键大全(转载) VC6快捷键大全,记在这里,方便查阅.F1: 帮助Ctrl+O :OpenCtrl+P :PrintCtrl+N :NewCtrl+Shift+F2 :清除所有书签F2 :上 ...

  2. 【转】 Pro Android学习笔记(三九):Fragment(4):基础小例子-续

    目录(?)[-] Step 3实现简介显示类DetailFragment 创建实例 编写所需的生命周期代码 Step 4实现showDetailint index如何管理fragment fragme ...

  3. ruby中特殊的全局变量

    全局变量:由$开头,可以在程序的任何位置访问到.在初始化前,全局变量有一个特殊的值 nil. 这里列出了一些以$打头并跟单个字符的特殊变量,包括主要的系统变量以及它们的含义: $! 最近一次的错误信息 ...

  4. javaScript之深度理解原型链

    经过多次的翻阅书籍终于对原型链在实际代码中的应用有了新的认识,但是不知道是否有错误的地方,还请大神多多指教. 构造函数.原型和实例的关系:每个构造函数都有一个原型对象funName.prototype ...

  5. Shrio00 Shiro认证登录、权限管理环境搭建

    基础环境准备: JDK -> java version "1.8.0_101" MAVEN -> Apache Maven 3.5.0 1 导入依赖 mysql驱动 m ...

  6. Window 显示鼠标的坐标

    Window 显示鼠标的坐标 GetCursorPos(POINT *p)函数 windows.h头文件里面的GetCursorPos(POINT *p)函数是用来获取鼠标在屏幕中的坐标信息的. Ge ...

  7. LEADTOOLS V19: 世界领先的图像处理开发工具包强势来袭

      投递人 itwriter 发布于 2014-12-22 16:04 评论(0) 有214人阅读   原文链接  [收藏]   « » LEAD 科技于 2014 年 12 月 11 日发布 LEA ...

  8. SpringMVC的国际化

    关于SpringMVC的国际化,http://www.cnblogs.com/liukemng/p/3750117.html这篇文章已经讲的很好了.它讲了有如下几种国际化方式 1:基于Http的hea ...

  9. 微信小程地图片未加载成功的情况 Failed to load local image resource

    在开发小程序的时候,发现在加载图片时并没有异常,但是后台却报错了. 例如以下我的一段代码: <view class="useage2 "> <image src= ...

  10. wpf listboxitem添加下划线

    1.通过List<string>进行赋值,没有字段绑定 // 前台xaml <ListBox x:Name="list1"> <ListBox.Ite ...