C# Design Patterns (5) - Prototype

本帖介绍 Prototype Pattern (原型模式),并以一个「人事招聘程序」作为示例来说明。

--------------------------------------------------------
本帖的示例下载点:
http://files.cnblogs.com/WizardWu/090713.zip
第一个示例为 Console Mode (控制台应用程序) 项目,第二个示例为 ASP.NET 网站项目。
执行示例需要 Visual Studio 2008 或 IIS + .NET 3.0,不需要数据库。
--------------------------------------------------------

Prototype Pattern (原型模式)

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

 

原型模式就是以一个既有的原型实例当作范本,利用复制的方式,动态获得这个原型实例的状态以及全部的字段和属性,以此创建一或多个相同的对象,而且不需要知道任何创建的细节。

Prototype 模式打个通俗的比方:假如您在图书馆看到几本自己喜欢的书籍,当看到某些知识点时,想在上面作相关记号,但由于其是图书馆的书,不能在上面乱涂乱画。此时您只好把相关的章节,用复印机把它复印出来,然后在自己复印的纸张上作记号。在 Prototype Pattern 里,Clone 方法就如同此种复印的动作,用户从一个既有的原型实例 (如同图书馆里的书),复印后得到一或多个新的拷贝,不会破坏原本的原型,且用户不必知道原型的内容和格式。

Prototype 也具有一种「展示」的意味,就像是车展上的「原型」车款。当您对某个车款感兴趣时,您可购买相同的车款,而不是车展上展示的那辆车。

在软件设计方面,也常需要进行此种对象复制。例如我们要写一套室内设计软件,软件的操作界面上有一条 Toolbar,用户只要单击 Toolbar 上的 Button,或用鼠标拖曳到设计窗格中,就可创建一个桌子或椅子的副本,并可事后改变它的颜色或位置。当设计师改变设计图中的副本对象时,Toolbar 上的「原型」对象并不会跟着被改变。同样的观念,亦适用于工业设计 CAD 软件、图像处理软件,以及 Visual Studio 等各种软件的设计。


Prototype 模式的重点在于 Clone 方法,它负责复制 (克隆) 出一个新的对象并返回,而不是用 new 运算符和某个类的构造函数去创建实例。在此模式中,派生类如何覆写父类的 Clone 方法将是重点。而 clone 的方式,又可分为「浅拷贝 (shallow copy)」和「深拷贝 (deep copy)」,在介绍这个 Prototype 模式之前,先简单介绍一下这两种拷贝方式的差异 [1], [2], [3], [4] :

  • 浅拷贝; 浅表复制 (shallow copy):对象拷贝时,如果字段是「值类型 (Value Type)」,则直接复制其值 (亦即复制整个字段);若字段为「引用类型 (Reference Type)」,则只复制其「引用 (reference; pointer)」,但不复制引用的字段,亦即若更改了任一个副本对象的某一个「引用类型」字段,则原型正本对象、其他副本对象,也全部会一并更改 (如同本帖的第三个示例 02_Employee / 02_ShallowCopy_fail.ASPx.cs),也就是说正本和所有的副本,都指向了内存的同一个位置。
  • 深拷贝; 深层复制 (deep copy):不论对象的字段为「值类型」或「引用类型」,都会完整地复制,而且这些字段和属性都是完全独立的。在深拷贝中,所有的对象都是重复的。

另补充,.NET 的类型系统,分为「值类型」、「引用类型」两种,其对象在内存中的存储方式不同,如下:

  • 值类型:只需要一段单独的内存,用于存储实际的数据在「栈 (Stack)」里,例如:int、byte、float、double、bool、struct、enum、char、...等类型。
  • 引用类型:需要两段内存,第一段存储实际的数据,其总是位于「堆 (Heap)」中;第二段是一个存在「栈」里的引用 (reference; pointer),其指向数据在「堆」中的实际存放位置,例如:object、string、class (包括自定义类)、interface、delegate、array (参考本帖的第三、第四个示例) 等类型。

但 string (字符串) 较特殊。string 虽然是「引用类型」,但却拥有「值类型」的特性。在 Prototype Pattern 及本帖的四个示例中,当透过 MemberwiseClone 方法做「浅拷贝」时,对象的 string 字段仍会被完整地复制,其结果就如同 int 等「值类型」的字段一样。


如下图 1 及下方示例 01_Shell,我们可透过自定义的 Prototype 抽象类,搭配 .NET 最顶层基类 System.Object 的 MemberwiseClone 方法,达成对象的「浅拷贝」,亦即复制某个对象其所有「字段 (field)」的值;但在 .NET 中,亦可舍弃此一自定义抽象类,让图 1 中的 ConcretePrototype1 类、ConcretePrototype2 类,改为实现 .NET 原生的 System.ICloneable 接口,透过实现此接口唯一的一个 Clone 方法,来达成对象的「浅拷贝」或「深拷贝」。


图 1 此图为 Prototype 模式的经典类图

 

01_Shell / Program.cs

NET技术C# Design Patterns (5) - Prototype,转载需保留来源!

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。