C#编程中使用设计模式中的原型模式的实例讲解

一、引言
在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,如果我们用new操作符去创建这样的类实例,这未免会增加创建类的复杂度和耗费更多的内存空间,因为这样在内存中分配了多个一样的类实例对象,然后如果采用工厂模式来创建这样的系统的话,随着产品类的不断增加,导致子类的数量不断增多,反而增加了系统复杂程度,所以在这里使用工厂模式来封装类创建过程并不合适,然而原型模式可以很好地解决这个问题,因为每个类实例都是相同的,当我们需要多个相同的类实例时,没必要每次都使用new运算符去创建相同的类实例对象,此时我们一般思路就是想——只创建一个类实例对象,如果后面需要更多这样的实例,可以通过对原来对象拷贝一份来完成创建,这样在内存中不需要创建多个相同的类实例,从而减少内存的消耗和达到类实例的复用。 然而这个思路正是原型模式的实现方式。下面就具体介绍下设计模式中的原型设计模式。

二、原型模式的详细介绍
我们来看一个入学考试场景实例

基对象(一般为接口,抽象类):考试题(样卷)

原型模式的复职克隆:根据需要印刷考卷,这里的考卷都是复制考试题样卷

客户端:学生答卷,同一套试卷,学生做题不可能一模一样

类图:

接口:试卷样例代码

  /// <summary>
  /// 选答题
  /// </summary>
  public class SelectTest
  {
    private string other;
    public string 你老婆多大
    {
      get
      {
        return this.other;
      }
      set
      {
        this.other = value;
      }
    }
  }
  /// <summary>
  /// 面试题
  /// </summary>
  public interface Itest
  {
    Itest Clone();

    string 知道设计模式吗
    {
      get;
      set;
    }
    string 设计模式有几种
    {
      get;
      set;
    }
    string 你知道那些
    {
      get;
      set;
    }
    SelectTest 附加题
    {
      get;
      set;
    }

    Test Test
    {
      get;
      set;
    }

    Test Test1
    {
      get;
      set;
    }
  }

复制克隆:复印机

 /// <summary>
  /// 继承Itest接口
  /// </summary>
  public class Test : Itest
  {
    private string one;
    private string two;
    private string three;
    private SelectTest other=new SelectTest();
    public string 知道设计模式吗
    {
      get
      {
        return this.one;
      }
      set
      {
        this.one = value;
      }
    }
    public string 设计模式有几种
    {
      get
      {
        return this.two;
      }
      set
      {
        this.two = value;
      }
    }
    public string 你知道那些
    {
      get
      {
        return this.three;
      }
      set
      {
        this.three = value;
      }
    }
    public SelectTest 附加题
    {
      get
      {
        return this.other;
      }
      set
      {
        this.other = value;
      }
    }
    #region IColorDemo 成员

    public Itest Clone()
    {
      //克隆当前类
      return (Itest)this.MemberwiseClone();
    }
    #endregion
  }

客户端,发卷做题

 static void Main()
    {
      //印刷试卷
      Itest test = new Test();
      //复制样本试卷
      Itest test1 = test.Clone();

      //考生1
      test.设计模式有几种 = "23";
      test.附加题.你老婆多大 = "18";

      //考生2
      test1.设计模式有几种 = "24";
      test1.附加题.你老婆多大 = "20";

      //显示考生答卷内容
      Console.WriteLine("test设计模式有几种:" + test.设计模式有几种);  //23
      Console.WriteLine("test附加题.你老婆多大:" + test.附加题.你老婆多大);  //20
      Console.WriteLine("test1设计模式有几种:" + test1.设计模式有几种);  //24
      Console.WriteLine("test1附加题.你老婆多大:" + test1.附加题.你老婆多大); //20

      Console.ReadKey();
    }

注意:这里两个人答得不一样,为什么附加题中,老婆年龄都为20?

这里涉及到深拷贝,浅拷贝问题,值类型是放在栈上的,拷贝之后,会自会在站上重新add一个,而class属于引用类型,拷贝之后,栈上重新分配啦一个指针,可指针却指向同一个位置的资源。浅拷贝,只拷贝值类型,深拷贝,引用类型也拷贝复制。

解决方案:

 public Itest Clone()
    {
      //克隆当前类
      Itest itst= (Itest)this.MemberwiseClone();
      SelectTest st = new SelectTest();
      st.你老婆多大 = this.other.你老婆多大;
      itst.附加题 = st;
      return itst;

    }

使用序列化解决

/// <summary>
  /// 选答题
  /// </summary>
  [Serializable]
  public class SelectTest
  {
    private string other;
    public string 你老婆多大
    {
      get
      {
        return this.other;
      }
      set
      {
        this.other = value;
      }
    }
  }
  /// <summary>
  /// 面试题
  /// </summary>
  public interface Itest
  {
    Itest Clone();

    string 知道设计模式吗
    {
      get;
      set;
    }
    string 设计模式有几种
    {
      get;
      set;
    }
    string 你知道那些
    {
      get;
      set;
    }
    SelectTest 附加题
    {
      get;
      set;
    }

  }

  /// <summary>
  /// 继承Itest接口
  /// </summary>

  public class Test : Itest
  {
    private string one;
    private string two;
    private string three;
    private SelectTest other=new SelectTest();
    public string 知道设计模式吗
    {
      get
      {
        return this.one;
      }
      set
      {
        this.one = value;
      }
    }
    public string 设计模式有几种
    {
      get
      {
        return this.two;
      }
      set
      {
        this.two = value;
      }
    }
    public string 你知道那些
    {
      get
      {
        return this.three;
      }
      set
      {
        this.three = value;
      }
    }
    public SelectTest 附加题
    {
      get
      {
        return this.other;
      }
      set
      {
        this.other = value;
      }
    }

    public Itest Clone()
    {
      SerializableHelper SerializableHelper = new 原型模式.SerializableHelper();
      string target = SerializableHelper.Serializable(this);
      return SerializableHelper.Derializable<Itest>(target); 

    }

  }

 public class SerializableHelper
  {
    public string Serializable(object target)
    {
      using (MemoryStream stream = new MemoryStream())
      {
        new BinaryFormatter().Serialize(stream, target);

        return Convert.ToBase64String(stream.ToArray());
      }
    }

    public object Derializable(string target)
    {
      byte[] targetArray = Convert.FromBase64String(target);

      using (MemoryStream stream = new MemoryStream(targetArray))
      {
        return new BinaryFormatter().Deserialize(stream);
      }
    }

    public T Derializable<T>(string target)
    {
      return (T)Derializable(target);
    }
  }

这就是对原型模式的运用。介绍完原型模式的实现代码之后,下面看下原型模式的类图,通过类图来理清原型模式实现中类之间的关系。具体类图如下:

三、原型模式的优缺点

原型模式的优点有

原型模式向客户隐藏了创建新实例的复杂性
原型模式允许动态增加或较少产品类。
原型模式简化了实例的创建结构,工厂方法模式需要有一个与产品类等级结构相同的等级结构,而原型模式不需要这样。
产品类不需要事先确定产品的等级结构,因为原型模式适用于任何的等级结构

原型模式的缺点有:
每个类必须配备一个克隆方法
配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。

四、.NET中原型模式的实现
在.NET中可以很容易地通过实现ICloneable接口(这个接口就是原型,提供克隆方法,相当于与上面代码中MonkeyKingPrototype抽象类)中Clone()方法来实现原型模式,如果我们想我们自定义的类具有克隆的功能,首先定义类继承与ICloneable接口并实现Clone方法。在.NET中实现了原型模式的类如下图所示(图中只截取了部分,可以用Reflector反编译工具进行查看):

(0)

相关推荐

  • C# 设计模式系列教程-适配器模式

    1. 概述 将一个类的接口转换成客户希望的另外一个接口.Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作. 2. 解决的问题 即Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作. 3. 模式中的角色 3.1 目标接口(Target):客户所期待的接口.目标可以是具体的或抽象的类,也可以是接口. 3.2 需要适配的类(Adaptee):需要适配的类或适配者类. 3.3 适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接

  • C# 设计模式系列教程-工厂方法模式

    1. 概述: 定义一个用于创建对象的接口,让子类决定实例化哪一个类.工厂方法使一个类的实例化延迟到子类. 2. 模式中的角色 2.1 抽象工厂(Creator):这个抽象类(或接口)声明一个创建对象的工厂方法,用来返回一个Product类型的对象. 2.2 具体工厂(ConcreteCreator):重定义工厂方法,返回一个具体的Concrete Product实例. 2.3 抽象产品(Product):定义工厂方法所创建的对象. 2.4 具体产品(ConcreteProduct): 具体产品,

  • C# 设计模式系列教程-简单工厂模式

    1. 概述: 将一个具体类的实例化交给一个静态工厂方法来执行,它不属于GOF的23种设计模式,但现实中却经常会用到 2. 模式中的角色 2.1 工厂类(Simple Factory): 只包含了创建具体类的静态方法. 2.2 抽象产品(Product):定义简单工厂中要返回的产品. 2.3 具体产品(ConcreteProduct):具体产品. 3. 模式解读 3.1 简单工厂模式的一般化类图 3.2 简单工厂模式的代码实现 /// <summary> /// 简单工厂类,用sealed修饰,

  • C# 设计模式系列教程-原型模式

    1. 概述 通过复制一个已经存在的实例来创建一个新的实例.被复制的实例被称为原型,这个原型是可定制的. 2. 模式中的角色 2.1 抽象原型类(Abstract Prototype):提供一个克隆接口 2.2 具体原型类(Concrete Prototype): 及实现了克隆接口的具体原型类 3. 实例:求职网站上现在都支持多份简历,如果每创建一份简历都要从头至尾地填写一遍,那也是非常让人沮丧的事.其实针对我们的求职岗位的不同,不同的简历可能只要修改局部内容就可以了,而不用全部重新构建一份新的简

  • C# 设计模式系列教程-组合模式

    1. 概述 将对象组合成树形结构以表示"部分-整体"的层次结构.组合模式使得用户对单个对象和组合对象的使用具有一致性. 2. 解决的问题 当希望忽略单个对象和组合对象的区别,统一使用组合结构中的所有对象(将这种"统一"性封装起来). 3. 组合模式中的角色 3.1 组合部件(Component):它是一个抽象角色,为要组合的对象提供统一的接口. 3.2 叶子(Leaf):在组合中表示子节点对象,叶子节点不能有子节点. 3.3 合成部件(Composite):定义有枝

  • C# 设计模式系列教程-代理模式

    1. 概述 为其它对象提供一种代理以控制对这个对象的访问. 解决的问题:如果直接访问对象比较困难,或直接访问会给使用者或系统带来一系列问题.这样对于客户端(调用者)来说,就不需要直接与真实对象进行交互,解除了调用者与真实对象的耦合. 2. 模式中的角色 2.1 抽象实体(Subject):定义了真实实体(RealSubject)和代理(Proxy)的公共接口,这样就在任何时候使用真实实体(RealSubject)的地方使用代理(Proxy). 2.2 代理(Proxy):保存一个引用使得代理可以

  • C# 设计模式系列教程-策略模式

    在讲策略模式之前,我先给大家举个日常生活中的例子,从首都国际机场到XXX酒店,怎么过去?1)酒店接机服务,直接开车来接.2)打车过去.3)机场快轨+地铁 4)机场巴士 5)公交车 6)走路过去(不跑累死的话) 等等.使用方法,我们都可以达到从机场到XXX酒店的目的,对吧.那么我所列出的从机场到XXX酒店的的方法,就是我们可以选择的策略. 再举个例子,就是我们使用WCF时,往往避免不了对它进行扩展,例如授权,我们可以通过自定义授权来扩展WCF.这里我们可以通过自定义AuthorizationPol

  • C# 设计模式系列教程-桥接模式

    1. 概述 将抽象部分(Abstraction)与实现部分(Implementor)分离,使它们可以独立地变化. 2. 解决的问题 在软件系统中,有些类型由于自身的逻辑,它具有两个或多个维度的变化.为了解决这种多维度变化,又不引入复杂度,这就要使用Bridge模式. 3. 模式中的角色 2.1 抽象(Abstraction):定义抽象接口,该接口中包含实现具体行为.具体特征的Implementor接口. 2.2 提炼的抽象(RefinedAbstraction):继承自Abstraction的子

  • C# 设计模式系列教程-单例模式

    1. 描述: 保证一个类仅有一个实例,并提供一个访问它的全局访问点. 2. 单例模式主要有3个特点,: 2.1 单例类确保自己只有一个实例. 2.2 单例类必须自己创建自己的实例. 2.3 单例类必须为其他对象提供唯一的实例. 3. 实现方式:懒汉单例类和饿汉单例类 3.1 懒汉式单例类 对于懒汉模式,我们可以这样理解:该单例类非常懒,只有在自身需要的时候才会行动,从来不知道及早做好准备.它在需要对象的时候,才判断是否已有对象,如果没有就立即创建一个对象,然后返回,如果已有对象就不再创建,立即返

  • C# 设计模式系列教程-装饰模式

    1. 概述 动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更灵活. 原理:增加一个修饰类包裹原来的类,包裹的方式一般是通过在将原来的对象作为修饰类的构造函数的参数.装饰类实现新的功能,但是,在不需要用到新功能的地方,它可以直接调用原来的类中的方法.修饰类必须和原来的类有相同的接口. 2. 模式中的角色 2.1 抽象构建(Component):定义一个抽象接口,用以给这些对象动态地添加职责. 2.2 具体构建(ConcreteComponent):定义一个具体的对象,也可以

随机推荐