學習設計模式《八》——原型模式

一、基礎概念

????????原型模式的本質是【克隆生成對象】;

????????原型模式的定義:用原型實例指定創建對象的種類,并通過拷貝這些原型創建新的對象

????????原型模式的功能:?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.pswp.cn/diannao/82147.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/82147.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/82147.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

olmOCR - PDF文檔處理工具包

文章目錄 一、關于 olmOCR相關資源包含內容團隊 二、安裝三、本地使用示例查看結果多節點/集群使用管道完整文檔 一、關于 olmOCR olmOCR 是用于訓練語言模型處理PDF文檔的工具包&#xff0c;支持大規模PDF文本解析和轉換。 相關資源 源碼&#xff1a;https://github.com/all…

Android開發補充內容

Android開發補充內容 fragment通信生命周期 Okhttp基本使用websocket Retrofit基本使用 RxJava基本使用定時任務 Hilt基本使用進階使用例子 組件庫Material ComponentsJetpack Compose fragment 通信 fragment于activity通信的一種原生方法是使用Bundle&#xff1a; Bundle …

隱私計算框架FATE二次開發心得整理(工業場景實踐)

文章目錄 版本介紹隱私計算介紹前言FATE架構總體架構FateBoard架構前端架構后端架構 FateClient架構創建DAG方式DAG生成任務管理python SDK方式 FateFlow架構Eggroll架構FATE算法架構Cpn層FATE ML層 組件新增流程新增組件流程新增算法流程 版本介紹 WeBank的FATE開源版本 2.2.…

AI驅動的制造工藝:系統化探索與創新

DeepSeek 技術全景 在當今 AI 技術蓬勃發展的時代,DeepSeek 已成為該領域中一顆耀眼的明星。自 2023 年 7 月 17 日成立以來,這家由知名私募巨頭幻方量化孕育而生的公司,迅速在 AI 領域嶄露頭角 。DeepSeek 的目標是開發頂尖的大語言模型(LLM),并利用數據蒸餾技術打造更精…

【嵌入式開發-LCD】

嵌入式開發-LCD ■ LCD簡介 ■ LCD簡介

java反射(2)

package 反射;import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays;public class demo {public static void main(String[] args) throws Exception {// 通過類的全限定名獲取對應的 Class 對象…

使用 Cesium 構建 3D 地圖應用的實踐

CesiumJS 是一個功能強大的開源 JavaScript 庫&#xff0c;能夠幫助開發者快速構建高性能、高精度的 3D 地球和地圖應用 。本文將介紹如何使用 Cesium 構建一個基本的 3D 地圖應用&#xff0c;并加載自定義的 3D Tiles 模型。 初始化 Cesium Viewer 首先&#xff0c;在 Vue 的…

結合Splash與Scrapy:高效爬取動態JavaScript網站

在當今的Web開發中&#xff0c;JavaScript的廣泛應用使得許多網站的內容無法通過傳統的請求-響應模式直接獲取。為了解決這個問題&#xff0c;Scrapy開發者經常需要集成像Splash這樣的JavaScript渲染引擎。本文將詳細介紹Splash JS引擎的工作原理&#xff0c;并探討如何將其與S…

企業級可觀測性實現:OpenObserve云原生平臺的本地化部署與遠程訪問解析

文章目錄 前言1. 安裝Docker2. 創建并啟動OpenObserve容器3. 本地訪問測試4. 公網訪問本地部署的OpenObserve4.1 內網穿透工具安裝4.2 創建公網地址 5. 配置固定公網地址 前言 嘿&#xff0c;各位小伙伴們&#xff0c;今天要給大家揭秘一個在云原生領域里橫掃千軍的秘密法寶—…

將本地項目提交到新建的git倉庫

方式一: # 登錄git&#xff0c;新建git倉庫和指定的分支&#xff0c;如master、dev# 下載代碼&#xff0c;默認下載master分支 git clone http://10.*.*.67/performance_library/pfme-*.git # 切換到想要提交代碼的dev分支 git checkout dev# 添加想要提交的文件 git add .#…

.NET平臺用C#在PDF中創建可交互的表單域(Form Field)

在日常辦公系統開發中&#xff0c;涉及 PDF 處理相關的開發時&#xff0c;生成可填寫的 PDF 表單是一種常見需求&#xff0c;例如員工信息登記表、用戶注冊表、問卷調查或協議確認頁等。與靜態 PDF 不同&#xff0c;帶有**表單域&#xff08;Form Field&#xff09;**的文檔支持…

在macOS上安裝windows系統

使用Boot Camp 1. 準備工作&#xff1a;確認Mac滿足Boot Camp系統要求&#xff0c;準備好Windows安裝光盤或ISO映像文件&#xff0c;以及一個至少8GB的空白USB閃存驅動器用于保存驅動程序。 2. 打開Boot Camp助理&#xff1a;在“應用程序”文件夾的“實用工具”中找到“Boot…

683SJBH基于J2EE的廣州旅游管理系統

第1章  緒論 課題背景 自互聯網internet成為一種革命性的大眾媒體以來&#xff0c;其發展速度之快令人驚嘆。而作為世界最大朝陽產業的旅游&#xff0c;當它與電子商務這一新興模式相結合時&#xff0c;其潛藏的商業價值表露無遺。根據CNN&#xff08;美國有線電視新聞網&…

前端面試每日三題 - Day 27

這是我為準備前端/全棧開發工程師面試整理的第27天每日三題練習&#xff0c;涵蓋了&#xff1a; CSS選擇器的優先級與權重計算機制Angular中的依賴注入&#xff08;Dependency Injection&#xff09;機制設計一個支持實時協作編輯&#xff08;如Google Docs&#xff09;的前端…

PostgreSQL數據庫操作SQL

數據庫操作SQL 創建 創建數據庫 create database db_test;創建并指定相關參數 with owner : 所有者encoding : 編碼connection limit &#xff1a;連接限制 create database db_test1 with owner postgresencoding utf-8connection limit 100;修改 修改數據庫名稱 renam…

JSP HTTP 狀態碼詳解

JSP HTTP 狀態碼詳解 引言 HTTP 狀態碼是 HTTP 協議的一部分,用于表示客戶端與服務器之間請求與響應的狀態。在 JavaServer Pages (JSP) 技術中,HTTP 狀態碼同樣扮演著重要的角色。本文將詳細解析 JSP 中的 HTTP 狀態碼,幫助開發者更好地理解和應用這些狀態碼。 HTTP 狀態…

文件一鍵解密軟件工具(支持pdf、word、excel、ppt、rar、zip格式文件)

一鍵解密解鎖神器支持解密pdf、doc、docx、xls、xlsx、ppt、pptx、rar、zip格式文件&#xff0c;Excel表格、Word文檔、PPT演示、RAR、ZIP壓縮包、PDF文檔一鍵輕松解密&#xff01;簡單/高效/安全。這款軟件由密碼帝官方提供&#xff0c;確保了其合法性和安全性&#xff0c;用戶…

Banana Pi BPI-CM6 是一款八核 RISC-V 模塊,兼容 Raspberry Pi CM 載板

Banana Pi BPI-CM6 是一款 SpacemIT K1 八核 RISC-V 系統級模塊&#xff0c;遵循 Raspberry Pi CM5 的設計&#xff0c;并提供高達 16GB LPDDR4 RAM、高達 128GB eMMC 閃存、千兆以太網控制器和 WiFi 6 藍牙 5.2 模塊。 BPI-CM6 雖然與 Raspberry Pi CM5 基本兼容&#xff0c…

【項目篇之統一硬盤操作】仿照RabbitMQ模擬實現消息隊列

統一硬盤操作 創建出實例封裝交換機的操作封裝隊列的操作封裝綁定的操作封裝消息的操作總的完整代碼&#xff1a; 我們之前已經使用了數據庫去管理交換機&#xff0c;綁定&#xff0c;隊列 還使用了數據文件去管理消息 此時我們就搞一個類去把上述兩個部分都整合在一起&#…

快速上手SpringBoot開發指南

文章目錄 1. 項目整體架構2. SpringBoot核心注解詳解2.1 應用程序入口注解SpringBootApplication 2.2 控制器層注解RestControllerRequestMappingPostMappingRequestBody 2.3 服務層注解ServiceAutowired 2.4 數據訪問層注解Repository 2.5 實體類注解JPA相關注解Lombok注解 3.…