2012年9月4日星期二

温故知新(3)原型模式

温故知新(3)原型模式

概述

说起原型模式,一般理解就是“克隆”一个对象,因此经常被人忽视。前两天看了一篇博文还有博文后面的讨论(http://www.cnblogs.com/winter-cn/archive/2009/12/02/1614987.html),并参考了GOF的解释,对原型模式有了一点新的体会。

GOF关于原型模式的意图是这样描述的:

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

这句话有两个子句,前一个说新对象的类型与原型对象的一致(用原型实例指定);后一个说创建新对象的方法是拷贝(克隆操作)。我们常常忽略的是第一个子句,既原型模式如何确定新建对象的类型。用通俗的说法解释一下就是,“给我一个和你一样的对象”。这里面的你就暗含了类型的确定,我可能不知道你的名字、成份、政治面貌,只知道你可以完成一个定特任务,所以我这时只要要求你提供一个和你一样类型的对象就可以了(你提供的对象当然要和你是同样类型的,否则不能保证可以完成任务)。

以下是GOF书中提到的原型模式的优点:

1、运行时增加和删除产品;

2、改变值以指定新对象;

3、改变结构以指定新对象;

4、减少子类构造;

5、用动态配置应用。

个人的观点的总结:

1、原型模式对客户端隐藏了具体的类型名,对新对象的创建逻辑进行了封装,避免客户端反复出现构造对象的代码;

2、原型模式避免了建立工厂类,代码比较简单。

3、从使用原型克隆的方式可能会带来效率的提升(比如支持内存复制的语言,或者构造对象需要包含IO操作等情况)

4、原型模式可以以动态的原型对象为模板构建新对象,提供了更好的灵活性。(比如可以从原型A复制一些对象,然再修改A,以修改后的A为原型新建一些对象)。

原型模式要求每一个原型类都必须实现“克隆”操作,通常“克隆”是指“深克隆”,但对于一些无需改变的固定子对象,则可以实现为“浅克隆”,类似享元模式,共享这些对象。

结构

原型模式的类图:

原型

原型模式的结构相对简单,只包含三种参与者:

1、克隆自身的原型接口——IPrototype;

2、原型接口的实现类——Prototype;

3、客户端代码——Client;

此外.NET Framework 中提供的ICloneable接口,并不能完整的表达原型模式(具体讨论,详见上面提到博文)。

示例

业务场景:假设我们有一个在线文档编辑系统,系统为提供了文档模板,用户可以从模板开始编写自己的文档,而不需要每次从头开始。

假设模板对象加载的消耗是比较大的,因此只加载一次模板,用户使用时可以从模板复制一个新的文档对象进行编辑(复制操作成本相对低廉),是一种合理的方式。此处采用原型模式进行实现。

1、定义原型接口IPrototype<T>。接口进行了泛型的类型约束。

 1:  using System;
 2:   
 3:  namespace DesignPatterns.Prototype
 4:  {
 5:      /// <summary>
 6:      /// 原型接口
 7:      /// </summary>
 8:      /// <typeparam name="T">根据原型对象创建的新对象,类型与原型对象相同</typeparam>
 9:      public interface IPrototype<T> where T : IPrototype<T>
10:      {
11:          T Clone();
12:      }
13:  }
14:   

2、文档类Document,实现上文提到的原型接口。

 1:  using System;
 2:   
 3:  namespace DesignPatterns.Prototype
 4:  {
 5:      /// <summary>
 6:      /// 文档
 7:      /// </summary>
 8:      public class Document : IPrototype<Document>
 9:      {
10:          /// <summary>
11:          /// 标题
12:          /// </summary>
13:          public string Title { get; set; }
14:   
15:          /// <summary>
16:          /// 正文
17:          /// </summary>
18:          public string Context { get; set; }
19:   
20:          /// <summary>
21:          /// 表格
22:          /// </summary>
23:          public Table Table { get; set; }
24:   
25:          /// <summary>
26:          /// 打印方法
27:          /// </summary>
28:          public void Print()
29:          {
30:              Console.WriteLine(this.Title);
31:              Console.WriteLine(this.Context);
32:              Console.WriteLine(this.Table);
33:          }
34:   
35:          /// <summary>
36:          /// 克隆方法,接口实现
37:          /// </summary>
38:          /// <returns>新的文档对象</returns>
39:          public Document Clone()
40:          {
41:              Document doc = new Document() { Title = this.Title, Context = this.Context };
42:              doc.Table = new Table() { Name = this.Table.Name, RowCount = this.Table.RowCount, ColumnCount = this.Table.ColumnCount };
43:              return doc;
44:          }
45:      }
46:  }
47:   

3、Document类中包含一个复杂类型属性Table,Table类的实现如下:

 1:  using System;
 2:   
 3:  namespace DesignPatterns.Prototype
 4:  {
 5:      /// <summary>
 6:      /// 表格
 7:      /// </summary>
 8:      public class Table
 9:      {
10:          /// <summary>
11:          /// 表名
12:          /// </summary>
13:          public string Name { get; set; }
14:   
15:          /// <summary>
16:          /// 行数
17:          /// </summary>
18:          public int RowCount { get; set; }
19:   
20:          /// <summary>
21:          /// 列数
22:          /// </summary>
23:          public int ColumnCount { get; set; }
24:   
25:          public override string ToString()
26:          {
27:              return string.Format("{0}: {1}行,{2}列", this.Name, this.RowCount, this.ColumnCount);
28:          }
29:      }
30:  }
31:   

4、客户端代码。客户端代码中用LoadTemplate方法模拟了模板的加载行为。

 1:  using System;
 2:   
 3:  namespace DesignPatterns.Prototype
 4:  {
 5:      class Program
 6:      {
 7:          static void Main(string[] args)
 8:          {
 9:              var template = LoadTemplate();
10:              template.Print();
11:              var copy = template.Clone();
12:              copy.Print();
13:   
14:              copy.Title = "2012年8月业绩统计";
15:              copy.Context = "环球贸易公司2012年8月业绩统计。。。";
16:              copy.Table.RowCount = 61;
17:              template.Print();
18:              copy.Print();
19:   
20:              Console.WriteLine("按任意键结束...");
21:              Console.ReadKey();
22:          }
23:   
24:          /// <summary>
25:          /// 模板文档的加载(假设存在IO等复杂操作)
26:          /// </summary>
27:          /// <returns></returns>
28:          private static Document LoadTemplate()
29:          {
30:              Document doc = new Document() { Title = "X年X月业绩统计", Context = "XXX公司X年X月业绩统计。。。" };
31:              doc.Table = new Table() { Name = "业绩统计表", RowCount = 45, ColumnCount = 7 };
32:              return doc;
33:          }
34:      }
35:  }
36:   

5、运行,查看结果。

image


TAG: