文档章节

设计模式(5) 原型模式

zhixin9001
 zhixin9001
发布于 07/09 22:12
字数 1249
阅读 38
收藏 0

行业解决方案、产品招募中!想赚钱就来传!>>>

  • 原型模式
  • 原型模式的适用场景
  • 浅拷贝
  • 深拷贝
  • 用Initialize方法修改初始化状态

原型模式与之前学习的各种工厂方法、单例模式、建造者模式最大、最直观的区别在于,它是从一个既有的对象“克隆”出新的对象,而不是从无到有创建一个全新的对象。与对文件的拷贝类似,原型模式是基于现有的对象拷贝新的对象。

原型模式

GOF对原型模式的描述为: Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.. — Design Patterns : Elements of Reusable Object-Oriented Software

原型模式的构造对象的的过程是,选择一个现成对象(原型对象),通过调用它的“克隆”方法来获得一个和它一样的对象。 其UML类图为: 原型模式 UML类图

原型模式的适用场景

原型模式适用与如下场景:

  • Factory、Builder、Singleton返回的都是“初始状态”的对象,但有的时候需要的对象反而是处于某种状态的对象;
  • 如果一个对象的初始化需要很多其他对象的数据准备或其他资源的繁琐计算,则可以使用原型模式直接克隆;
  • 当需要一个对象的大量公共信息,少量字段进行个性化设置的时候,也可以使用原型模式拷贝出现有对象的副本进行加工处理。

浅拷贝

拷贝有浅拷贝与深拷贝之分,浅拷贝会复制值类型的字段,但对于引用类型则只复制引用,相当于原型与副本仍然共用同一个引用类型,所以浅拷贝是不彻底的。 而深拷贝则与之相反,深拷贝会开辟一块另外的内存区域,把原型包括值类型和引用类型都逐位复制过去。

在C#中其实有内置的原型模式支持,object类型自带的MemberwiseClone方法实现的是浅拷贝,还有ICloneable接口,在实现这个接口时可以自行决定拷贝的深度。

基于浅拷贝实现的原型模式:

public interface IProtoType
{
    IProtoType Clone();
    string Name { get; set; }
}
public class ConcretePrototype : IProtoType
{
    public string Name { get; set; }

    public IProtoType Clone()
    {
        return (IProtoType)this.MemberwiseClone();
    }
}

深拷贝

深拷贝是把引用目标地址的内容逐个bit地复制一份,看起来简单,但实现起来并不容易,因为成员可能是引用类型,而且可能存在引用类型的嵌套,最正规的方法是通过反射不断深入嵌套结构的内部,类似对树的遍历,碰到引用类型,就重新new一个。还有一种比较取巧的方法是利用二进制、Json、XML等序列化、反序列化来实现。但序列化方式的性能很差,如果拷贝的次数较多,这个劣势会更加明显。 二进制序列化方式相比其他两种更加灵活,可以通过NonSerializedAttribute设置序列化时忽略的属性。 通过二进制序列化来拷贝:

[Serializable]
public class DeepClone : IProtoType
{
    [NonSerialized]
    public List<string> Users = new List<string>();
    public string Name { get; set; }

    public IProtoType Clone()
    {
        string graph = SerializeHelper.BinarySerialize(this);
        return SerializeHelper.BinaryDeSerialize<IProtoType>(graph);
    }
}

//序列化工具类
public class SerializeHelper
{
    public static string BinarySerialize(object graph)
    {
        using (MemoryStream memoryStream = new MemoryStream())
        {
            IFormatter formatter = new BinaryFormatter();
            formatter.Serialize(memoryStream, graph);
            Byte[] arrGraph = memoryStream.ToArray();
            return Convert.ToBase64String(arrGraph);
        }
    }


    public static T BinaryDeSerialize<T>(string graph)
    {
        Byte[] arrGraph = Convert.FromBase64String(graph);
        using (MemoryStream memoryStream = new MemoryStream(arrGraph))
        {
            IFormatter formatter = new BinaryFormatter();
            return (T)formatter.Deserialize(memoryStream);
        }
    }
}

用Initialize方法修改初始化状态

有些时候,客户程序需要的不仅仅是千篇一律的副本,还要求副本的某些属性具有不同的状态,之前原型模式的适用场景也提到过当需要一个对象的大量公共信息,少量字段进行个性化设置的时候,面对这种需求,如果想要增加几个Clone方法的重载,就会破坏设计模式封装变化的初衷,如果后续还有其他的初始化需求,增加更多的重载方法是不现实的。 《设计模式:可复用面向对象软件的基础》对此有一个建议,就是增加一个名为Initialize()的操作,把不稳定性转嫁到这个方法上。在C#还可以借助params类型特性,支持可变参数。 调用端在Clone操作之后,再调用Initialize方法,并把指定的初始化参数传入以设定对象的内部状态。

参考书籍: 王翔著 《设计模式——基于C#的工程化实现及扩展》

zhixin9001
粉丝 6
博文 150
码字总数 177010
作品 0
西安
私信 提问
加载中
请先登录后再评论。
Swift百万线程攻破单例(Singleton)模式

一、不安全的单例实现 在上一篇文章我们给出了单例的设计模式,直接给出了线程安全的实现方法。单例的实现有多种方法,如下面: class SwiftSingleton { } 这段代码的实现,在shared中进行条...

一叶博客
2014/06/20
3.3K
16
StreetPass

StreetPass,最初是想模拟任天堂NDS掌机中的StreetPass开发的(但未完成,大体框架已可行)。适用于记录每天在街上偶遇的纸妹Or帅锅的信息。 原理是想采用Wifi hot技术,由此可以在搜索到附近...

口米巴
2013/03/18
1.5K
0
简单CMS

主要修改: 1)增加文章模块,文章列表显示在首页和单品页中; 2)增加店铺模块,店铺显示在首页和瀑布流页中; 3)增加网站地图模块; 4)增加sitemap模块; 5)增加第三方淘宝登录功能; ...

简单CMS
2012/12/25
4.2K
0
php开源框架--CorePHP

简介: CorePHP框架是一个快速,安全,灵活的php开源框架,主要是为了简化和快速开发小型项目和开源系统二次开发而诞生。它既可以完美的支持MVC模式,又可以不受限制的支持传统编程模式。它是...

shooke
2012/12/27
2.8K
1
cgi模式 web服务器--LiuGinx

liugnix是一个 cgi模式 web服务器。 支持语言 因使用的为CGI 模式,支持所有cgi模式运行的程序。 php python go perl 如何运行 cd mywebsite liugnix_ENV liuginx --help2013/05/07 01:10:43...

Able
2013/05/07
872
0

没有更多内容

加载失败,请刷新页面

加载更多

MySql大表分页(附独门秘技)

问题背景 MySql(InnoDB)中的订单表需要按时间顺序分页查询,且主键不是时间维度递增,订单表在百万以上规模,此时如何高效地实现该需求? 注:本文并非主要讲解如何建立索引,以下的分析均建...

osc_8kei32r9
10分钟前
0
0
css中使用变量

2017年3月,微软宣布 Edge 浏览器将支持 CSS 变量。 这个重要的 CSS 新功能,所有主要浏览器已经都支持了。 声明css变量的时候,变量名前面要加两根连词线(--)。 变量名大小写敏感,--hea...

osc_mpdswsal
11分钟前
0
0
WAS 忘记密码

一、重置密码 1.首先关闭was,ps –ef|grep java 查看java进程号,然后kill -9 XXXX杀掉进程即可。或者使用命令./stopServer.sh server1 2.取消控制台安全验证 方法一:/opt/IBM/WebSphere/...

osc_1i3ltp99
12分钟前
0
0
npm install的--save选项是什么? - What is the --save option for npm install?

问题: I saw some tutorial where the command was: 我看到了一些命令所在的教程: npm install --save What does the --save option mean? --save选项是什么意思? Not able to find the......

fyin1314
13分钟前
0
0
C#使用读写锁三行代码简单解决多线程并发写入文件时线程同步的问题

在开发程序的过程中,难免少不了写入错误日志这个关键功能。实现这个功能,可以选择使用第三方日志插件,也可以选择使用数据库,还可以自己写个简单的方法把错误信息记录到日志文件。 选择最...

osc_7cws6vmd
13分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部