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

作者:张龙豪 时间:2023-07-18 07:47:10 

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

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

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

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

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

类图:

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

接口:试卷样例代码


 /// <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);
   }
 }

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

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

三、原型模式的优缺点

原型模式的优点有

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

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


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

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

标签:C#,设计模式
0
投稿

猜你喜欢

  • C#使用日志组件log4net

    2022-11-05 03:48:15
  • java实现上传图片并压缩图片大小功能

    2023-06-14 22:32:35
  • WinForm实现为TextBox设置水印文字功能

    2023-06-09 21:15:38
  • 详解java CountDownLatch和CyclicBarrier在内部实现和场景上的区别

    2022-03-22 13:03:02
  • 使用SpringBoot跨系统调用接口的方案

    2022-10-14 21:35:18
  • 新手小白用C# winform 读取Excel表的实现

    2022-12-27 15:45:12
  • Android XUtils3框架的基本使用方法(二)

    2021-08-03 16:32:45
  • 使用mutex实现应用程序单实例运行代码分享

    2023-09-18 22:45:11
  • 详解如何使用Java编写图形化的窗口

    2022-04-18 03:25:52
  • Java Bean 作用域及它的几种类型介绍

    2022-12-02 20:39:42
  • spring中的注解事务演示和添加步骤详情

    2023-03-03 08:32:48
  • C++作用域与函数重载的实现

    2022-04-30 06:28:30
  • Base64编码解码原理及C#编程实例

    2022-05-07 03:58:53
  • 聊聊@RequestBody和Json之间的关系

    2023-11-27 03:31:45
  • android xml实现按钮的圆角、阴影效果及按下变化效果的实现代码

    2022-07-30 23:23:12
  • AndroidStudio3.6.1打包jar及AndroidStudio4.0打包jar的一系列问题及用法

    2021-09-05 14:34:33
  • Spring使用@Autowired为抽象父类注入依赖代码实例

    2023-02-01 09:30:35
  • Java接口默认方法带来的问题分析【二义性问题】

    2023-11-27 20:32:55
  • Android中方法数超限问题与启动优化详解

    2023-03-05 17:55:41
  • GC调优实战之高分配速率High Allocation Rate

    2021-09-30 22:03:21
  • asp之家 软件编程 m.aspxhome.com