唐睿

A full stack engineer, an architecture, a project coordinator and an entrepreneur, who is always learning.

设计模式 — ProtoType

22 Jan 2006 » 技术
编程 设计模式 GoF C#

问题:当我们在编写大型应用程序的时候,可能会遇到这样的情况:构造了一个基类,然后从该基类派生出子类。在子类的数目比较少的情况下,没有任何事情会发 生,但是如果当从某一个基类派生出的子类达到一定数目的时候,维护这些子类将是一件非常繁复的工作。

解决方案:既然如此,干脆就不派生子类。无论如何,子类都是具有特性的基类的衍生,我们可以将这种特性直接在基类中反映出来。具体说来,就是实现不同种类的克隆。

方法:传递进相关特性的参数,得到具有该特性的对象。

其它:本示例代码以 C# 为示例语言,通过实现 ICloneable 和 ISerializable 来实现对象的浅表复制 (Shallow copy) 和深层复制 (Deep copy) 。这同时也是学习序列化的很好的演示示例。

附件:一个 XML 文档:

<?xml version="1.0" encoding="utf-8"?>
<employees>
  <employee>
    <firstname>Rui</firstname>
    <lastname>Tang</lastname>
  </employee>
  <employee>
    <firstname>Lichao</firstname>
   <lastname>Mu</lastname>
  </employee>
</employees>
using System;
using System.IO;
using System.Xml;
using System.Collections;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace ProtoType {
  /// <summary>
  /// 用于存储每个员工的基本信息
  /// </summary>
  public class Employee {
    private string firstName;
    private string lastName;

    public string FirstName {
      get { return this.firstName; }
      set { this.firstName = value; }
    }

    public string LastName {
      get { return this.lastName; }
      set { this.lastName = value; }
    }

    /// <summary>
    /// 默认构造函数
    /// </summary>
    public Employee() { }

    /// <summary>
    /// 从序列化信息中得到数据
    /// </summary>
    /// <param name="info"/>序列化信息
    /// <param name="context"/>
    /// <param name="index"/>序列化信息中字典的索引
    public Employee(SerializationInfo info, StreamingContext context, int index) {
      this.firstName = info.GetString("emp_fname" + index.ToString());
      this.lastName = info.GetString("emp_lname" + index.ToString());
    }

    /// <summary>
    /// 将数据添加到序列化信息中
    /// </summary>
    /// <param name="info"/>序列化信息
    /// <param name="context"/>
    /// <param name="index"/>序列化信息中字典的索引
    public void GetObjectData(SerializationInfo info, StreamingContext context, int index) {
      o.AddValue("emp_fname" + index.ToString(), firstName);
      info.AddValue("emp_lname" + index.ToString(), lastName);
    }
  }

  /// <summary>
  /// 处理员工信息的类
  /// </summary>
  [Serializable]
  public class EmployeeProcess : ICloneable,ISerializable {
    /// <summary>
    /// 存储所有的员工
    /// </summary>
    private ArrayList employeeArray = new ArrayList();

    /// <summary>
    /// 默认构造函数,读取XML文档,并将员工添加到employeeArray中
    /// </summary>
    public EmployeeProcess() {
      XmlDocument xmlDoc = new XmlDocument();
      Employee objEmp = null;

      xmlDoc.Load("Employees.xml");
      foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes) {
        objEmp = new Employee();
        objEmp.FirstName = node.SelectSingleNode("FirstName").InnerText;
        objEmp.LastName = node.SelectSingleNode("LastName").InnerText;
        employeeArray.Add(objEmp);
      }
    }

    /// <summary>
    /// 反序列化必须要实现的一个方法,用于在反序列化时构造对象
    /// </summary>
    /// <param name="info"/>序列化信息
    /// <param name="context"/>
    public EmployeeProcess(SerializationInfo info, StreamingContext context) {
      int count;
      string temp = string.Empty;
      Employee objEmp = null;

      count = int.Parse(info.GetValue("emp_count",temp.GetType()).ToString());
      for (int i=0; i<count ; i++) {
        objEmp = new Employee(info, context, i);
        employeeArray.Add(objEmp);
      }
    }

    /// <summary>
    /// 实现浅表复制的克隆方法
    /// 
    /// <returns>返回对象的引用</returns>
    public object Clone() {
      try { return this; }
      catch (Exception exce) {
        Console.WriteLine(exce.Message);
        return null;
      }
    }

    /// <summary>
    /// 实现深层复制的克隆方法
    /// </summary>
    /// <param name="isDeep"/>判断是否请求深层复制的标志量
    /// <returns>返回对象的深层副本</returns>
    public object Clone(bool isDeep) {
      try {
        if (isDeep) { return CreateDeepCopy(); }
        else { return this.Clone(); }
      } catch (Exception exce) {
        Console.WriteLine(exce.Message);
        return null;
      }
    }

    /// <summary>
    /// 序列化必须要实现的一个方法,将数据写到序列化信息中
    /// </summary>
    /// <param name="info"/>序列化信息
    /// <param name="context"/>
    public void GetObjectData(SerializationInfo info, StreamingContext context) {
      Employee objEmp;
      info.AddValue("emp_count",employeeArray.Count);
      for (int i=0; i<employeearray .Count; i++) {
        objEmp = (Employee)employeeArray[i];
        objEmp.GetObjectData(info,context,i);
      }
    }

    /// <summary>
    /// 枚举所有员工的信息
    ///
    /// <returns>员工信息</returns>
    public string GetEmployeeInfo() {
      string strEmpData = string.Empty;
      for (int i=0; i</employeearray><employeearray .Count; i++) {
        strEmpData = strEmpData + ((Employee)employeeArray[i]).FirstName + " " + ((Employee)employeeArray[i]).LastName + "\n";
      }
      return strEmpData;
    }

    /// <summary>
    /// 改变员工信息,用于测试深层复制和浅表复制的异同
    /// 
    public void ChangeEmployeeInfo() {
      foreach (Employee objEmp in employeeArray) {
        objEmp.FirstName = "FirstName";
        objEmp.LastName = "LastName";
      }
    }

    /// <summary>
    /// 通过序列化创建对象的深层副本
    /// </summary>
    /// <returns>返回对象的深层副本</returns>
    private EmployeeProcess CreateDeepCopy() {
      EmployeeProcess objEmpCopy;
      Stream objStream;
      BinaryFormatter objBinFormatter = new BinaryFormatter();

      try {
        objStream = File.Open("Employees.bin",FileMode.Create);
        objBinFormatter.Serialize(objStream, this);
        objStream.Close();

        objStream = File.Open("Employees.bin",FileMode.Open);
        objEmpCopy = (EmployeeProcess)objBinFormatter.Deserialize(objStream);
        objStream.Close();

        return objEmpCopy;
      } catch (Exception exce) {
        Console.WriteLine(exce.Message);
        return null;
      }
    }
  }

  /// <summary>
  /// 测试ProtoType
  /// </summary>
  class TestProtoType {
    [STAThread]
    static void Main(string[] args) {
      // 用正常的方法创建一个对象
      EmployeeProcess mainEmployeeProcess = new EmployeeProcess();
      // 输出该对象的信息
      Console.WriteLine(mainEmployeeProcess.GetEmployeeInfo());

      // 通过浅表复制得到一个刚才创建的对象的副本
      EmployeeProcess childEmployeeProcess1 = (EmployeeProcess)mainEmployeeProcess.Clone();
      // 通过深层复制得到一个刚才创建的对象的副本
      EmployeeProcess childEmployeeProcess2 = (EmployeeProcess)mainEmployeeProcess.Clone(true);
      // 改变用正常方法得到的对象的信息
      mainEmployeeProcess.ChangeEmployeeInfo();
      // 输出浅表副本对象的信息,应该已经改变
      Console.WriteLine(childEmployeeProcess1.GetEmployeeInfo());
      // 输出深成副本对象的信息,和原始的一样
      Console.WriteLine(childEmployeeProcess2.GetEmployeeInfo());

      Console.Read();
    }
  }
}