当前位置: 首页 > ai >正文

学习设计模式《八》——原型模式

一、基础概念

        原型模式的本质是【克隆生成对象】;

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

        原型模式的功能: 1、通过克隆来创建新的对象实例; 2、为克隆出来的新对象实例复制原型实例属性值;

        克隆:无论是自己实现克隆方法,还是采用C#提供的克隆方法,都存在一个浅度克隆和深度克隆的问题:

        1、浅度克隆:只负责克隆按值传递的数据(比如基本数据类型、String类型);

        2、深度克隆除了浅度克隆要克隆的值外,还负责克隆引用类型的数据,基本上就是被克隆实例所有的属性数据都会被克隆出来

原型模式的优缺点
序号原型模式的优点原型模式的缺点
1

对客户端隐藏具体的实现类型

(即:原型模式的客户端只知道原型接口类型,并不知道具体的实现类型, 从而减少了客户端对具体实现类型的依赖)

每个原型的子类都必须实现克隆操作,尤其在包含引用类型的对象时,克隆方法会比较麻烦,必须要能够递归地让所有相关对象都要正确地实现克隆
2

在运行时动态改变具体的实现类型

(即:原型模式可以在运行期间,由客户来注册符合原型接口的实现类型,也可以动态改变具体的实现类型,看起来接口没有任何变化,但其实运行的已经是另外一个类实例了【因为克隆一个原型就类似于实例化一个类】)

        何时选用原型模式?

                1、如果一个系统想要独立于它想要使用的对象时【让系统只面向接口编程,在系统需要新对象时可以通过克隆原型获取】;
                2、如果需要实例化的类是在运行时动态指定的,可通过克隆原型类得到想要的实例

二、原型模式示例

        业务需求:比如我们有一个订单处理功能,需要保存订单业务(在这个业务功能中,每当订单的预订数量超过1000的时候,就需要将订单拆分为两份订单保存;如果拆成了两份订单后,数量还是超过1000,则继续拆分,直到每份订单的数量不超过1000);且这个订单类型会分为两种(一种是个人订单;一种是公司订单),无论何种订单类型都需要按照业务规则处理。

 2.1、不使用模式的示例

        既然有两种订单类型,且都要实现保存订单相关业务的通用功能,那么我们可以定义一个接口来声明这些功能行为,然后在定义具体的类分别实现即可:

1、定义订单接口

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace PrototypePattern
{/// <summary>/// 订单接口/// </summary>internal interface IOrder{//获取订单产品数量int GetOrderProductNumber();//设置订单产品数量void SetOrderProductNumber(int productNumber);}//Interface_end
}

2、分别定义个人订单与企业订单类来实现接口定义的功能行为

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace PrototypePattern
{/// <summary>/// 个人订单对象/// </summary>internal class PersonalOrder : IOrder{//消费者名称public string? CustomerName;//产品编号public string? ProductId;//产品订单数量private int productOrderNumber = 0;public int GetOrderProductNumber(){return productOrderNumber;}public void SetOrderProductNumber(int productNumber){//做各种逻辑校验内容,此处省略this.productOrderNumber = productNumber;}public override string ToString(){string str=$"个人订单的订购人是【{CustomerName},订购的产品是【{ProductId}】,订购数量是【{productOrderNumber}】】";return str;}}//Class_end
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace PrototypePattern
{/// <summary>/// 企业订单对象/// </summary>internal class EnterpriseOrder : IOrder{//企业名称public string? EnterpriseName;//产品编号public string? ProductId;//产品的订单数量private int productOrderNumber=0;public int GetOrderProductNumber(){return productOrderNumber;}public void SetOrderProductNumber(int productNumber){productOrderNumber = productNumber;}public override string ToString(){string str = $"企业订单的订购企业是【{EnterpriseName},订购的产品是【{ProductId}】,订购数量是【{productOrderNumber}】】";return str;}}//Class_end
}

3、现在的中心任务就是要实现《保存订单》的业务方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace PrototypePattern
{/// <summary>/// 处理订单的业务对象/// </summary>internal class OrderBussiness{//固定的数量private const int fixedNumber = 1000;/// <summary>/// 保存订单/// </summary>/// <param name="order">订单</param>public void SaveOrder(IOrder order){/*根据业务要求,当预订产品的数量大于1000时,就需要把订单拆分为两份*///1、判断订单是否大于1000(若大于1000则拆分订单)while (order.GetOrderProductNumber()>1000){//2.创建一份新订单,这份订单传入的订单除了数量不一样,其他都相同IOrder newOrder = null;if (order is PersonalOrder){//创建相应的新订单对象PersonalOrder newPO = new PersonalOrder();//将传入订单的数据赋值给新订单对象PersonalOrder po = (PersonalOrder)order;newPO.CustomerName = po.CustomerName;newPO.ProductId = po.ProductId;newPO.SetOrderProductNumber(fixedNumber);//将个人订单对象内容赋值给新订单newOrder = newPO;}else if (order is EnterpriseOrder){ EnterpriseOrder newEO = new EnterpriseOrder();EnterpriseOrder eo = (EnterpriseOrder)order;newEO.EnterpriseName = eo.EnterpriseName;newEO.ProductId = eo.ProductId;newEO.SetOrderProductNumber(fixedNumber);newOrder=newEO;}//3、设置拆分后的订单数量order.SetOrderProductNumber(order.GetOrderProductNumber() - fixedNumber);//4、处理业务功能Console.WriteLine($"拆分生成的订单是【{newOrder}】");}//订单数量不超过1000的直接执行业务处理Console.WriteLine($"拆分生成的订单是【{order}】");}}//Class_end
}

4、编写客户端测试

using System.Net.Sockets;namespace PrototypePattern
{internal class Program{static void Main(string[] args){OrderBussinessTest();Console.ReadLine();}/// <summary>/// 处理订单的业务对象测试/// </summary>private static void OrderBussinessTest(){Console.WriteLine("---处理订单的业务对象测试---");/*个人订单*/Console.WriteLine("\n\n个人订单\n");//创建订单对象并设置内容(为了演示简单直接new)PersonalOrder po = new PersonalOrder();po.CustomerName = "张三";po.ProductId = $"CK{new Random(Guid.NewGuid().GetHashCode()).Next(100,999)}";po.SetOrderProductNumber(2966);//获取订单业务对象(为了演示简单直接new)OrderBussiness ob=new OrderBussiness();//保存订单业务ob.SaveOrder(po);/*企业订单*/Console.WriteLine("\n\n企业订单\n");EnterpriseOrder eo=new EnterpriseOrder();eo.EnterpriseName = "牛奶咖啡科技有限公司";eo.ProductId= $"CK{new Random(Guid.NewGuid().GetHashCode()).Next(100, 999)}";eo.SetOrderProductNumber(3001);OrderBussiness ob2 = new OrderBussiness();ob2.SaveOrder(eo);}}//Class_end
}

5、运行结果如下:

 6、有何问题?

不使用模式的示例是实现了我们需要的保存订单业务功能;但是存在两个问题:

        《1》既然我们想要通用的保存订单业务功能,那么实现对象是不应该知道订单的具体对象和具体实现,更不能依赖订单的具体实现;而上面的示例很明显的依赖了具体对象和具体实现;

        《2》不使用模式的示例在实现业务功能的时候是很难扩展新的订单类型(即:如果我们现在又增加了几种订单类型,那么还需要在保存订单业务方法里面添新类型的处理,很繁琐,不优雅)。

 2.2、使用原型模式的示例

        其实上面不使用模式的示例暴露的问题总结起来就是:【我们已经有了具体的实例对象,如何能够在不修改业务方法的情况下快速的使用更多新增的对象】而原型模式刚好就是解决这个问题的。

1、定义订单接口规范产品功能行为

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace PrototypePattern.Prototype
{/// <summary>/// 订单接口/// </summary>internal interface IOrder{//获取订单产品数量int GetOrderProductNumber();//设置订单产品数量void SetOrderProductNumber(int productNumber);//克隆方法IOrder Clone();}//Interface_end
}

2、创建个人订单对象与企业订单对象继承接口实现具体功能行为

        注意:关于这里的克隆方法不能直接使用【return this】来写,这是因为若这样设置,那么每次克隆客户端获取的都是同一个实例,都指向同一个内存空间,此时只要修改克隆出来的实例对象就会影响到原型对象的实例,这是不可取的;正确地做法是:直接先new一个自己的对象实例,然后再把自己实例的数据取出来赋值到新对象实例中去】如下所示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace PrototypePattern.Prototype
{/// <summary>/// 个人订单对象/// </summary>internal class PersonalOrder : IOrder{//订购人员名称public string? CustomerName;//产品编号public string? ProductId;//订单产品数量private int productOrderNumber=0;public IOrder Clone(){//创建一个新订单对象,然后把该实例的数据赋值给新对象【浅度克隆】PersonalOrder po = new PersonalOrder();po.CustomerName = this.CustomerName;po.ProductId = this.ProductId;po.SetOrderProductNumber(this.productOrderNumber);return po;}public int GetOrderProductNumber(){return productOrderNumber;}public void SetOrderProductNumber(int productNumber){//做各种逻辑校验内容,此处省略this.productOrderNumber = productNumber;}public override string ToString(){string str = $"个人订单的订购人是【{CustomerName},订购的产品是【{ProductId}】,订购数量是【{productOrderNumber}】】";return str;}}//Class_end
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace PrototypePattern.Prototype
{/// <summary>/// 企业订单对象/// </summary>internal class EnterpriseOrder : IOrder{//企业名称public string? EnterpriseName;//产品编号public string? ProductId;//产品的订单数量private int productOrderNumber = 0;public IOrder Clone(){//创建一个新订单对象,然后把该实例的数据赋值给新对象【浅度克隆】EnterpriseOrder eo = new EnterpriseOrder();eo.EnterpriseName = this.EnterpriseName;eo.ProductId = this.ProductId;eo.SetOrderProductNumber(this.productOrderNumber);return eo;}public int GetOrderProductNumber(){return productOrderNumber;}public void SetOrderProductNumber(int productNumber){//做各种逻辑校验内容,此处省略this.productOrderNumber = productNumber;}public override string ToString(){string str = $"企业订单的订购企业是【{EnterpriseName},订购的产品是【{ProductId}】,订购数量是【{productOrderNumber}】】";return str;}}//Class_end
}

3、创建一个类构建通用的保存订单业务方法且不依赖具体的实例对象、方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace PrototypePattern.Prototype
{/// <summary>/// 处理订单的业务对象/// </summary>internal class OrderBussiness{private const int fixedNumber = 1000;public void SaveOrder(IOrder order){/*根据业务要求,当预订产品的数量大于1000时,就需要把订单拆分为两份*///1、判断订单是否大于1000(若大于1000则拆分订单)while (order.GetOrderProductNumber()>1000){//2、创建一份新的订单,除了订单的数量不一样,其他内容都一致IOrder newOrder = order.Clone();//3、然后进行赋值newOrder.SetOrderProductNumber(fixedNumber);//4、创建新订单后原订单需要将使用的数量减去order.SetOrderProductNumber(order.GetOrderProductNumber()-fixedNumber);//5、处理业务功能Console.WriteLine($"拆分生成的订单是【{newOrder}】");}//订单数量不超过1000的直接执行业务处理Console.WriteLine($"拆分生成的订单是【{order}】");}}//Class_end
}

4、客户端测试原型模式

using System.Net.Sockets;namespace PrototypePattern
{internal class Program{static void Main(string[] args){OrderBussinessPrototypeTest();Console.ReadLine();}/// <summary>/// 处理订单业务原型模式测试/// </summary>private static void OrderBussinessPrototypeTest(){Console.WriteLine("---处理订单业务原型模式测试---");/*个人订单*/Console.WriteLine("\n\n个人订单\n");//创建订单对象并设置内容(为了演示简单直接new)Prototype.PersonalOrder po = new Prototype.PersonalOrder();po.CustomerName = "张三";po.ProductId = $"CK{new Random(Guid.NewGuid().GetHashCode()).Next(100, 999)}";po.SetOrderProductNumber(2966);//获取订单业务对象(为了演示简单直接new)Prototype.OrderBussiness ob = new Prototype.OrderBussiness();//保存订单业务ob.SaveOrder(po);/*企业订单*/Console.WriteLine("\n\n企业订单\n");Prototype.EnterpriseOrder eo = new Prototype.EnterpriseOrder();eo.EnterpriseName = "牛奶咖啡科技有限公司";eo.ProductId = $"CK{new Random(Guid.NewGuid().GetHashCode()).Next(100, 999)}";eo.SetOrderProductNumber(3001);Prototype.OrderBussiness ob2 = new Prototype.OrderBussiness();ob2.SaveOrder(eo);}}//Class_end
}

5、运行结果
 

可以看到我们使用原型模式也成功实现了业务功能,并且我们现在扩展新的订单类型后也十分简单,直接用新订单类型实例调用业务方法即可,而不用对业务类方法进行任何修改。

 2.3、原型实例与克隆实例

  2.3.1、自己手动实现克隆方法

        原型实例与克隆出来的实例,本质上是不同的实例(克隆完成后,它们之间是没有关联,互不影响的,也就是说它们所指向不同的内存空间)如下所示

using System.Net.Sockets;namespace PrototypePattern
{internal class Program{static void Main(string[] args){TestPrototypeInstaceAndCloneInstace();Console.ReadLine();}/// <summary>/// 【浅度克隆】原型实例与克隆出来的实例,本质上是不同的实例(克隆完成后,它们之间是没有关联,互不影响的)/// </summary>private static void TestPrototypeInstaceAndCloneInstace(){//先创建原型实例Prototype.PersonalOrder order = new Prototype.PersonalOrder();//设置原型实例的订单数量order.SetOrderProductNumber(666);//为了演示简单,就只输出数量Console.WriteLine($"第一次获取个人订单对象实例的数量【{order.GetOrderProductNumber()}】");//通过克隆来获取实例Prototype.PersonalOrder order2 = (Prototype.PersonalOrder)order.Clone();//修改克隆实例的数量order2.SetOrderProductNumber(33);//输出数量Console.WriteLine($"克隆实例的数量【{order2.GetOrderProductNumber()}】");//输出原型实例的数量Console.WriteLine($"克隆实例修改数量后原型实例的数量是【{order.GetOrderProductNumber()}】");}}//Class_end
}

运行结果如下:

  2.3.2、C#中的克隆方法

在C#语言中已经提供了克隆方法,定义在Object类中;需要克隆功能的类,只需要继承【System.ICloneable】接口即可;如下为演示C#克隆方法示例:

ICloneable.Clone 方法 (System) | Microsoft Learnhttps://learn.microsoft.com/zh-cn/dotnet/api/system.icloneable.clone?view=net-7.0Object.MemberwiseClone 方法 (System) | Microsoft Learnhttps://learn.microsoft.com/zh-cn/dotnet/api/system.object.memberwiseclone?view=net-9.01、创建接口

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace PrototypePattern.CSharpClone
{/// <summary>/// 订单接口/// </summary>internal interface IOrder2{//获取订单产品数量int GetOrderProductNumber();//设置订单产品数量void SetOrderProductNumber(int productNumber);}//Interface_end
}

 2、创建具体的个人订单象继承订单接口与C#克隆接口实现功能

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace PrototypePattern.CSharpClone
{/// <summary>/// 订单对象【继承C#克隆接口】/// </summary>internal class PersonalOrder2 : IOrder2, ICloneable{//订购人员名称public string? CustomerName;//产品编号public string? ProductId;//订单产品数量private int productOrderNumber = 0;public object Clone(){//直接调用父类的克隆方法【浅度克隆】object obj = base.MemberwiseClone();return obj;}public int GetOrderProductNumber(){return this.productOrderNumber;}public void SetOrderProductNumber(int productNumber){//做各种逻辑校验内容,此处省略this.productOrderNumber = productNumber;}public override string ToString(){string str = $"个人订单的订购人是【{CustomerName},订购的产品是【{ProductId}】,订购数量是【{productOrderNumber}】】";return str;}}//Class_end
}

3、客户端调用测试

using PrototypePattern.PrototypeeManager;
using System.Net.Sockets;namespace PrototypePattern
{internal class Program{static void Main(string[] args){TestPrototypeInstaceAndCloneInstace2();Console.ReadLine();}/// <summary>/// 【浅度克隆】原型实例与克隆出来的实例,本质上是不同的实例(克隆完成后,它们之间是没有关联,互不影响的)/// </summary>private static void TestPrototypeInstaceAndCloneInstace2(){//先创建原型实例CSharpClone.PersonalOrder2 order = new CSharpClone.PersonalOrder2();//设置原型实例的订单数量order.SetOrderProductNumber(666);//为了演示简单,就只输出数量Console.WriteLine($"第一次获取个人订单对象实例的数量【{order.GetOrderProductNumber()}】");//通过克隆来获取实例CSharpClone.PersonalOrder2 order2 = (CSharpClone.PersonalOrder2)order.Clone();//修改克隆实例的数量order2.SetOrderProductNumber(33);//输出数量Console.WriteLine($"克隆实例的数量【{order2.GetOrderProductNumber()}】");//输出原型实例的数量Console.WriteLine($"克隆实例修改数量后原型实例的数量是【{order.GetOrderProductNumber()}】");}}//Class_end
}

运行结果:

 2.4、深度克隆

         深度克隆:除了浅度克隆要克隆的值外,还负责克隆引用类型的数据,基本上就是被克隆实例所有的属性数据都会被克隆出来(如果被克隆的对象里面属性数据是引用类型,也就是属性类型也是对象,则需要一直递归地克隆下去【也就是说,要想深度克隆成功,必须要整个克隆所涉及的对象都要正确实现克隆方法,如果其中的一个没有正确实现克隆,那么就会导致克隆失败】)。

  2.4.1、自己实现原型的深度克隆

1、定义产品接口规范产品行为功能

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace PrototypePattern.CSharpClone
{/// <summary>/// 定义一个克隆产品自身的接口/// </summary>internal interface IProductPrototype{//克隆产品自身的方法IProductPrototype CloneProduct();}//Interface_end
}

2、定义一个产品对象,继承产品接口并实现克隆功能

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace PrototypePattern.CSharpClone
{/// <summary>/// 产品对象/// </summary>internal class Product : IProductPrototype{//产品编号public string? ProductId;//产品名称public string? ProductName;public IProductPrototype CloneProduct(){//创建一个新订单,然后把本实例的数据复制过去Product product = new Product();product.ProductId = ProductId;product.ProductName = ProductName;return product;}public override string ToString(){string str = $"产品编号【{ProductId}】产品名称【{ProductName}】";return str;}}//Class_end
}

3、订单对象的添加产品对象属性


using PrototypePattern.CSharpClone;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace PrototypePattern.Prototype
{/// <summary>/// 个人订单对象/// </summary>internal class PersonalOrder4 : IOrder{//订购人员名称public string? CustomerName;//产品编号public string? ProductId;//产品对象【新增的产品对象引用类型】public CSharpClone.Product? Product;//订单产品数量private int productOrderNumber=0;public IOrder Clone(){//创建一个新订单对象,然后把该实例的数据赋值给新对象【浅度克隆】PersonalOrder4 po = new PersonalOrder4();po.CustomerName = this.CustomerName;po.ProductId = this.ProductId;po.SetOrderProductNumber(this.productOrderNumber);/*自己实现深度克隆也不是很复杂,但是比较麻烦,如果产品类中又有属性是引用类型,* 在产品类实现克隆方法的时候,则需要调用那个引用类型的克隆方法了。这样一层层的调用下去,* 如果中途有任何一个对象没有正确实现深度克隆,那将会引起错误*///对于对象类型的数据,深度克隆的时候需要继续调用整个对象的克隆方法【体现深度克隆】po.Product = (CSharpClone.Product)this.Product.CloneProduct();return po;}public int GetOrderProductNumber(){return productOrderNumber;}public void SetOrderProductNumber(int productNumber){//做各种逻辑校验内容,此处省略this.productOrderNumber = productNumber;}public override string ToString(){string str = $"个人订单的订购人是【{CustomerName},订购的产品是【{ProductId}】,订购数量是【{productOrderNumber}】】,产品对象是【{Product}】】";return str;}}//Class_end
}

4、客户端测试

using System.Net.Sockets;namespace PrototypePattern
{internal class Program{static void Main(string[] args){OrderBussinessTestDeepClone();Console.ReadLine();}/// <summary>/// 【深度克隆】处理订单的业务对象测试/// </summary>private static void OrderBussinessTestDeepClone(){Console.WriteLine("---处理订单的业务对象测试【深度克隆】---");/*个人订单*/Console.WriteLine("\n\n个人订单\n");//创建订单对象并设置内容(为了演示简单直接new)Prototype.PersonalOrder4 po = new Prototype.PersonalOrder4();po.CustomerName = "张三";po.ProductId = $"CK{new Random(Guid.NewGuid().GetHashCode()).Next(100, 999)}";po.SetOrderProductNumber(2966);//实例化产品类且指定所有属性的值CSharpClone.Product product = new CSharpClone.Product();product.ProductName = "产品1";product.ProductId = "XCKX006";//个人订单对象的产品赋值po.Product = product;Console.WriteLine($"这是第一次获取的个人订单对象实例【{po}】");//通过克隆来获取新实例Prototype.PersonalOrder4 po2 = (Prototype.PersonalOrder4)po.Clone();//修改克隆实例的值po2.CustomerName = "李四";po2.ProductId = $"2CK{new Random(Guid.NewGuid().GetHashCode()).Next(100, 999)}";po2.SetOrderProductNumber(3666);po2.Product.ProductName = "产品2";po2.Product.ProductId = "YYCKYY009";//输出克隆实例的Console.WriteLine($"输出克隆出来的个人订单对象实例【{po2}】");//再次输出原型的实例Console.WriteLine($"这是第二次获取的个人订单对象实例【{po}】");}}//Class_end
}

 运行结果如下:

        通过自己实现深度克隆可以了解其中原理;其实自己实现深度克隆也不是很复杂,只是比较麻烦。若产品类中又有属性是引用类型,在产品实现克隆方法的时候,则需要调用那个引用类型的克隆方法;需要这样一层层对的调用下去;但中途若有任何一个对象没有正确实现深度克隆,就会引起错误 。

  2.4.2、C#中的深度克隆

1、让产品对象继承C#的克隆接口【ICloneable】

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace PrototypePattern.CSharpClone
{/// <summary>/// 产品对象/// </summary>internal class Product2 : ICloneable{//产品编号public string? ProductId;//产品名称public string? ProductName;public object Clone(){//直接使用C#的克隆方法,不用自己手动给属性逐一赋值object obj = base.MemberwiseClone();return obj;}public override string ToString(){string str = $"产品编号【{ProductId}】产品名称【{ProductName}】";return str;}}//Class_end
}

2、实现个人订单对象添加产品属性内容

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace PrototypePattern.CSharpClone
{internal class PersonalOrder5 : IOrder2, ICloneable{//订购人员名称public string? CustomerName;//产品编号public string? ProductId;//产品对象【新增的产品对象引用类型】public Product2? Product2;//订单产品数量private int productOrderNumber = 0;public object Clone(){//直接调用C#的克隆方法【浅度克隆】PersonalOrder5 obj = (PersonalOrder5)base.MemberwiseClone();//必须手工针对每一个引用类型的属性进行克隆obj.Product2 = (Product2)this.Product2.Clone();return obj;}public int GetOrderProductNumber(){return this.productOrderNumber;}public void SetOrderProductNumber(int productNumber){//做各种逻辑校验内容,此处省略this.productOrderNumber = productNumber;}public override string ToString(){string str = $"个人订单的订购人是【{CustomerName},订购的产品是【{ProductId}】,订购数量是【{productOrderNumber}】,产品对象是【{Product2}】】";return str;}}//Class_end
}

3、客户端测试

using PrototypePattern.PrototypeeManager;
using System.Net.Sockets;namespace PrototypePattern
{internal class Program{static void Main(string[] args){OrderBussinessTestDeepClone2();Console.ReadLine();}/// <summary>/// 【深度克隆】处理订单的业务对象测试/// </summary>private static void OrderBussinessTestDeepClone2(){Console.WriteLine("---处理订单的业务对象测试【深度克隆】---");/*个人订单*/Console.WriteLine("\n\n个人订单\n");//创建订单对象并设置内容(为了演示简单直接new)CSharpClone.PersonalOrder5 po = new CSharpClone.PersonalOrder5();po.CustomerName = "张三";po.ProductId = $"CK{new Random(Guid.NewGuid().GetHashCode()).Next(100, 999)}";po.SetOrderProductNumber(2966);//实例化产品类且指定所有属性的值CSharpClone.Product2 product2 = new CSharpClone.Product2();product2.ProductName = "产品1";product2.ProductId = "XCKX006";//个人订单对象的产品赋值po.Product2 = product2;Console.WriteLine($"这是第一次获取的个人订单对象实例【{po}】");//通过克隆来获取新实例CSharpClone.PersonalOrder5 po2 = (CSharpClone.PersonalOrder5)po.Clone();//修改克隆实例的值po2.CustomerName = "李四";po2.ProductId = $"2CK{new Random(Guid.NewGuid().GetHashCode()).Next(100, 999)}";po2.SetOrderProductNumber(3666);po2.Product2.ProductName = "产品2";po2.Product2.ProductId = "YYCKYY009";//输出克隆实例的Console.WriteLine($"输出克隆出来的个人订单对象实例【{po2}】");//再次输出原型的实例Console.WriteLine($"这是第二次获取的个人订单对象实例【{po}】");}}//Class_end
}

4、运行结果如下:

 2.5、原型管理器

        如果一个系统中的原型数目不固定(如:原型可以被动态的创建和销毁)那么久需要再系统中维护一个当前可用的原型注册表(也称为原型管理器);有了原型管理器后,除了向原型管理器里面添加原型对象的时候是通过new来创建对象的,其余时候都是通过原型管理器来请求原型实例,然后通过克隆方法来获取新对象实例,就可以动态的管理原型了。

1、定义原型接口规范行为功能

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace PrototypePattern.PrototypeeManager
{/// <summary>/// 原型管理器接口/// </summary>internal interface IPrototypeManager{IPrototypeManager Clone();string GetName();void SetName(string name);}//Interface_end
}

2、定义类对象原型继承接口实现具体功能

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace PrototypePattern.PrototypeeManager
{internal class ConcreatePrototype1 : IPrototypeManager{private string name;public IPrototypeManager Clone(){ConcreatePrototype1 cp=new ConcreatePrototype1();cp.SetName(name);return cp;}public string GetName(){return name;}public void SetName(string name){this.name = name;}public override string ToString(){string str = $"这是具体的原型一,名称是【{name}】";return str;}}//Class_end
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace PrototypePattern.PrototypeeManager
{internal class ConcreatePrototype2 : IPrototypeManager{private string name;public IPrototypeManager Clone(){ConcreatePrototype2 cp=new ConcreatePrototype2();cp.SetName(name);return cp;}public string GetName(){return name;}public void SetName(string name){this.name = name;}public override string ToString(){string str = $"这是具体的原型二,名称是【{name}】";return str;}}//Class_end
}

3、实现原型管理器

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;namespace PrototypePattern.PrototypeeManager
{internal class PrototypeManager{//定义一个字典来记录原型编号与原型实例的对应关系private static Dictionary<string,IPrototypeManager> dicPrototype=new Dictionary<string,IPrototypeManager>();//私有化构造方法,避免外部私自创建实例private PrototypeManager(){}/// <summary>/// 添加原型/// </summary>/// <param name="prototypeId">原型编号</param>/// <param name="prototype">原型实例</param>public static void AddPrototype(string prototypeId,IPrototypeManager prototype){if (string.IsNullOrEmpty(prototypeId) || prototype == null){string str = $"原型编号或者原型不能为空,请检查后重试!";Console.WriteLine(str);return;}if (!dicPrototype.ContainsKey(prototypeId)){dicPrototype.Add(prototypeId, prototype);}else{string str = $"当前已经存在编号为【{prototypeId}】的原型【{prototype}】,不用重复添加!!!";Console.WriteLine(str);}}/// <summary>/// 删除原型/// </summary>/// <param name="prototypeId">原型编号</param>public static void DelPrototype(string prototypeId){if (string.IsNullOrEmpty(prototypeId)){string str = $"原型编号不能为空,请检查后重试!";Console.WriteLine(str);return;}dicPrototype.Remove(prototypeId);}/// <summary>/// 获取原型/// </summary>/// <param name="prototypeId">原型编号</param>/// <returns></returns>public static IPrototypeManager GetPrototype(string prototypeId){IPrototypeManager prototype = null;if (string.IsNullOrEmpty(prototypeId)){string str = $"原型编号不能为空,请检查后重试!";Console.WriteLine(str);return prototype;}if (dicPrototype.ContainsKey(prototypeId)){prototype = dicPrototype[prototypeId];return prototype;}else{Console.WriteLine($"你希望获取的原型还没注册或已被销毁!!!");return prototype;}}/// <summary>/// 修改原型/// </summary>/// <param name="prototypeId">原型编号</param>/// <param name="prototype">原型实例</param>public static void ModifyPrototype(string prototypeId, IPrototypeManager prototype){if (string.IsNullOrEmpty(prototypeId) || prototype == null){string str = $"原型编号或者原型不能为空,请检查后重试!";Console.WriteLine(str);return;}if (dicPrototype.ContainsKey(prototypeId)){dicPrototype[prototypeId] = prototype; ;}else{string str = $"当前不存在编号为【{prototypeId}】的原型,无法修改!!!";Console.WriteLine(str);}}}//Class_end
}

4、客户端使用原型管理器

using PrototypePattern.PrototypeeManager;
using System.Net.Sockets;namespace PrototypePattern
{internal class Program{static void Main(string[] args){PrototypeManagerTest();Console.ReadLine();}/// <summary>/// 原型管理器测试/// </summary>private static void PrototypeManagerTest(){Console.WriteLine("---原型管理器测试---");//初始化原型管理器string prototypeId = "原型一";IPrototypeManager pm = new ConcreatePrototype1();PrototypeManager.AddPrototype(prototypeId,pm);//1、获取原型来创建对象IPrototypeManager pm1 = PrototypeManager.GetPrototype(prototypeId).Clone();pm1.SetName("张三");Console.WriteLine($"第一个实例是【{pm1}】");//2、有人动态的切换string prototypeId2 = "原型二";IPrototypeManager pm2 = new ConcreatePrototype2();PrototypeManager.AddPrototype(prototypeId2,pm2);//3、重新获取原型创建对象IPrototypeManager pm3 = PrototypeManager.GetPrototype(prototypeId2).Clone();pm3.SetName("李四");Console.WriteLine($"第二个实例是【{pm3}】");//4、有人注销了原型PrototypeManager.DelPrototype(prototypeId);//5、再次获取原型一来创建对象IPrototypeManager pm4 = PrototypeManager.GetPrototype(prototypeId).Clone();pm4.SetName("王五");Console.WriteLine($"第三个实例是【{pm4}】");}}//Class_end
}

5、运行结果:

三、项目源码工程

kafeiweimei/Learning_DesignPattern: 这是一个关于C#语言编写的基础设计模式项目工程,方便学习理解常见的26种设计模式https://github.com/kafeiweimei/Learning_DesignPattern

http://www.xdnf.cn/news/4682.html

相关文章:

  • 基于互信息分解表示学习的多模态情感分析
  • Dense 与 MoE 系列模型架构的全面对比与应用策略
  • git可视化工具Fork软件的详细使用教程
  • QTDesinger如何给label加边框
  • QT:获取软件界面窗口的尺寸大小2025.5.8
  • 【特别版】Kubernetes集群安装(1master,2node)
  • 蓝绿发布与金丝雀发布
  • 系统架构设计-真题2024下半年总结
  • Android 位掩码操作(和~和|的二进制运算)
  • 【MySQL】(10)用户和权限管理
  • 基于CNN与SHAP可解释性分析的神经网络回归预测模型【MATLAB】
  • 面试问题总结(回忆版)
  • Matter协议,智能家居生态平台的“共生契约”
  • 【Redis】持久化与事务
  • 机器学习与深度学习
  • 图表制作-折柱混合
  • 【办公类-99-05】20250508 D刊物JPG合并PDF便于打印
  • TensorFlow 2.x入门实战:从零基础到图像分类项目
  • 【数据融合实战手册·应用篇】“数字孪生+视频融合”让智慧城市拥有空间感知
  • 手机隐私数据彻底删除工具:回收或弃用手机前防数据恢复
  • 美团、京东、阿里博弈下的冷思考
  • leetcode0279. 完全平方数-medium
  • 手写 Vue 源码 === 依赖清理机制详解
  • 使用 merge_asof 实现高效的时间序列匹配(无需循环)
  • rest_framework学习之认证 权限
  • 【软件设计师:数据库】13.数据库控制与安全
  • vite 代理 websocket
  • 稳定性_李雅普诺夫——Lyapunov直接法
  • 网络靶场基础知识
  • 是更换Window资源管理器的时候了-> Files-community/Files