原型模式(8)

来源:http://www.cnblogs.com/xiaomowang/archive/2017/01/17/6290486.html
-Advertisement-
Play Games

今天我們來講一下原型模式。老樣子,我們先舉一個案例: 一、案例 我們找工作,需要投簡歷,一份簡歷是不夠的,我們需要多複製幾分簡歷。 好,我們用簡單的控制台程式來完成上述的需求。 二、演繹 1、第一步演繹 客戶端調用: OK,我們實現了上述的功能,搞了三份簡歷出來。我們可以通過複製粘貼,複製更多分簡歷 ...


今天我們來講一下原型模式。老樣子,我們先舉一個案例:

一、案例

我們找工作,需要投簡歷,一份簡歷是不夠的,我們需要多複製幾分簡歷。

好,我們用簡單的控制台程式來完成上述的需求。

二、演繹

1、第一步演繹

 1     /// <summary>
 2     /// 簡歷類
 3     /// </summary>
 4     class Resume
 5     {
 6         private string name;//姓名
 7         private string sex;//性別
 8         private string age;//年齡
 9         private string timeArea;//工作時間
10         private string company;//工作單位
11         
12         public Resume(string name)
13         {
14             this.name = name;
15         }
16         //設置個人信息
17         public void SetPersonalInfo(string sex, string age)
18         {
19             this.sex = sex; 
20             this.age = age;
21         }
22         //設置工作經歷
23         public void SetWorkExperience(string timeArea, string company)
24         {
25             this.timeArea = timeArea;
26             this.company = company;
27         }
28         //顯示
29         public void Display()
30         {
31             Console.WriteLine($"{name}{sex}{age}");
32             Console.WriteLine($"工作經歷:{timeArea}{company}");
33         }
34     }

客戶端調用:

 1         public static void Main()
 2         {
 3             Resume a = new Resume("小魔王");
 4             a.SetPersonalInfo("", "29");
 5             a.SetWorkExperience("2000-2000", "XX公司");
 6 
 7             Resume b = new Resume("小魔王");
 8             b.SetPersonalInfo("", "29");
 9             b.SetWorkExperience("2000-2000", "XX公司");
10 
11             Resume c = new Resume("小魔王");
12             c.SetPersonalInfo("", "29");
13             c.SetWorkExperience("2000-2000", "XX公司");
14 
15             a.Display();
16             b.Display();
17             c.Display();
18             Console.Read();
19         }

OK,我們實現了上述的功能,搞了三份簡歷出來。我們可以通過複製粘貼,複製更多分簡歷出來。

那麼問題來了。

①需要三份簡歷,客戶端就需要實例化三次,如果需要20份簡歷,100份簡歷,那麼客戶端豈不是要瘋掉了。

②如果我簡歷上寫錯了一個字,年齡29要改成30,那麼客戶端豈不是需要修改3次,20份簡歷的話,就需要修改20次,好恐怖啊。

2、第二步演繹

針對上述問題,我們將代碼做一下修改:

我們將客戶端進行一下優化:

 1             Resume a = new Resume("小魔王");
 2             a.SetPersonalInfo("", "29");
 3             a.SetWorkExperience("2000-2000", "XX公司");
 4             Resume b = a;
 5             Resume c = a;
 6             a.Display();
 7             b.Display();
 8             c.Display();
 9             Console.Read();
10         }

哈哈,這樣的話,省卻了不少的代碼呢。

好,我們來看一看他的本質, Resume b = a;Resume c = a; 實際上是傳的引用,而不是傳值,就如同,有b、c兩張紙寫著簡歷在a處一樣,實際沒有任何內容。

好,那麼接下來,我們正式的給大家介紹一下原型模式,他很好的解決了上述的問題。

 什麼是原型模式呢?

gof 給了我們很好的定義:原型模式,用原型實例指定創建對象的種類,並通過拷貝這些原型創建新的對象。

說白了,原型模式就是從一個對象創造另外一個可定製的對象,而且不需要知道任何的創造細節。

好,下麵我們來看一下最基本的原型模式的代碼

 1     /// <summary>
 2     /// 原型類
 3     /// </summary>
 4     internal abstract class Prototype
 5     {
 6         public string Id { get; }
 7 
 8         protected Prototype(string id)
 9         {
10             this.Id = id;
11         }
12         
13         public abstract Prototype Clone();
14     }
15     /// <summary>
16     /// 具體的原型方法
17     /// </summary>
18     class ConcretePrototype1 : Prototype
19     {
20         public ConcretePrototype1(string id) : base(id)
21         {
22 
23         }
24         //克隆
25         public override Prototype Clone()
26         {
27             return (Prototype) this.MemberwiseClone();//MemberwiseClone()這個方法大家可以去百度谷歌一下。
28         }
29     }

客戶端調用

1         public static void Main()
2         {
3             ConcretePrototype1 p1 = new ConcretePrototype1("I");
4             ConcretePrototype1 c1 = (ConcretePrototype1) p1.Clone();
5             Console.WriteLine($"Cloned:{c1.Id}");
6             Console.ReadKey();
7         }

好了,上面就是原型模式的基本代碼,核心就是一個Clone() 方法。

因為就克隆而言,在我們的日常編碼中,太常用了,所以,.net在System命名空間中提供了一個ICloneable的介面,這個介面中有一個唯一的方法Clone(),跟我們的抽象類Prototype很相似,所以,以後我們想要實現原型模式,就不用費力去寫Prototype這個抽象類了,只需要將具體的原型類繼承ICloneable這個介面即可,就能實現原型模式了。

好,下麵我們就將我們文章開頭中提到的案例用原型模式來寫一遍。

3、第三步演繹

案例中我麽Resume類只需要繼承ICloneable介面,然後實現這個介面中的Clone()方法就可以了。其他的不用變。

 1 class Resume:ICloneable
 2     {
 3         private string name;//姓名
 4         private string sex;//性別
 5         private string age;//年齡
 6         private string timeArea;//工作時間
 7         private string company;//工作單位
 8         
 9         public Resume(string name)
10         {
11             this.name = name;
12         }
13         //設置個人信息
14         public void SetPersonalInfo(string sex, string age)
15         {
16             this.sex = sex; 
17             this.age = age;
18         }
19         //設置工作經歷
20         public void SetWorkExperience(string timeArea, string company)
21         {
22             this.timeArea = timeArea;
23             this.company = company;
24         }
25         //顯示
26         public void Display()
27         {
28             Console.WriteLine($"{name}{sex}{age}");
29             Console.WriteLine($"工作經歷:{timeArea}{company}");
30         }
31         //實現ICloneable介面中的Clone()方法
32         public object Clone()
33         {
34             return this.MemberwiseClone();
35         }
36     }

客戶端

 1         public static void Main()
 2         {
 3             //第一份簡歷
 4             Resume a = new Resume("小魔王");
 5             a.SetPersonalInfo("", "29");
 6             a.SetWorkExperience("2000-2000","XX公司");
 7 
 8             //生成一份新簡歷
 9             Resume b = (Resume) a.Clone();
10             //可以對新簡歷的細節進行修改。
11             b.SetWorkExperience("1998-1999","AA公司");
12 
13             Resume c = (Resume) a.Clone();            
14             c.SetPersonalInfo("", "28");
15 
16             a.Display();
17             b.Display();
18             c.Display();
19             Console.ReadKey();
20         }

好了,下麵我們來看一下這個原型模式有什麼好處。

相比之前的代碼,每new一次,就會調用一下構造函數,如果創造這個對象的過程很複雜,也就是說,構造函數很複雜的話,那麼調用構造函數執行起來就非常的慢,浪費很多的資源。這麼多次的執行這個初始化操作,效率實在是太低了。所以,一般在初始化i信息不變的情況下,克隆是最好的變法,既隱藏了對象創建的細節,又提高的效率。

原型模式就這麼結束了嗎?那我在給小伙伴們挖個坑來跳跳,哈哈。

現在 Resume中的欄位都是string類型的,也就是說都是值類型的。如果,我們將Resume中的欄位換成引用類型的,將會出現怎樣的效果呢?

來,我們來變一下。

我們增加一個工作經歷的類

1     class WorkExperience
2     {
3         public string workDate { get; set; }
4         public string company { get; set; }
5     }

然後,Resume類中就這麼寫了

 1     class Resume : ICloneable
 2     {
 3         private string name;//姓名
 4         private string sex;//性別
 5         private string age;//年齡
 6         private WorkExperience work;
 7 
 8         public Resume(string name)
 9         {
10             this.name = name;
11             work = new WorkExperience();
12         }
13         //設置個人信息
14         public void SetPersonalInfo(string sex, string age)
15         {
16             this.sex = sex;
17             this.age = age;
18         }
19         //設置工作經歷
20         public void SetWorkExperience(string timeArea, string company)
21         {
22             work.workDate = timeArea;
23             work.company = company;
24         }
25         //顯示
26         public void Display()
27         {
28             Console.WriteLine($"{name}{sex}{age}");
29             Console.WriteLine($"工作經歷:{  work.workDate}{ work.company}");
30         }
31         //實現ICloneable介面中的Clone()方法
32         public object Clone()
33         {
34             return this.MemberwiseClone();
35         }
36     }

客戶端:

 1         public static void Main()
 2         {
 3             //第一份簡歷
 4             Resume a = new Resume("小魔王");
 5             a.SetPersonalInfo("", "29");
 6             a.SetWorkExperience("2000-2000", "XX公司");
 7 
 8             //生成一份新簡歷
 9             Resume b = (Resume)a.Clone();
10             //可以對新簡歷的細節進行修改。
11             b.SetWorkExperience("1998-1999", "AA公司");
12 
13             Resume c = (Resume)a.Clone();
14             c.SetPersonalInfo("", "28");
15 
16             a.Display();
17             b.Display();
18             c.Display();
19             Console.ReadKey();
20         }

好,我們來運行一下。

嗯?有什麼不對嗎?這不是我預期的效果啊~

工作經歷結果就是我們最後設置的那個值,這是什麼原因呢? 我們可以查一下MemberwiseClone()就應該知道是什麼原因了。

好,那麼我們該如何解決這個問題呢?

1、將WorkExperience 類 實現 ICloneable 介面

1     class WorkExperience:ICloneable
2     {
3         public string workDate { get; set; }
4         public string company { get; set; }
5         public object Clone()
6         {
7             return MemberwiseClone();
8         }
9     }

2、修改Resume類

 1     class Resume : ICloneable
 2     {
 3         private string name;//姓名
 4         private string sex;//性別
 5         private string age;//年齡
 6         private WorkExperience work;
 7 
 8         public Resume(string name)
 9         {
10             this.name = name;
11             work = new WorkExperience();
12         }
13         //私有構造函數,以便克隆工作經歷的數據
14         private Resume(WorkExperience work)
15         {
16             this.work = (WorkExperience)work.Clone();
17         }
18         //設置個人信息
19         public void SetPersonalInfo(string sex, string age)
20         {
21             this.sex = sex;
22             this.age = age;
23         }
24         //設置工作經歷
25         public void SetWorkExperience(string timeArea, string company)
26         {
27             work.workDate = timeArea;
28             work.company = company;
29         }
30         //顯示
31         public void Display()
32         {
33             Console.WriteLine($"{name}{sex}{age}");
34             Console.WriteLine($"工作經歷:{  work.workDate}{ work.company}");
35         }
36         //實現ICloneable介面中的Clone()方法
37         public object Clone()
38         {
39             Resume obj = new Resume(this.work);//調用/私有構造函數,將工作經歷克隆。
40             obj.name = this.name;
41             obj.sex = this.sex;
42             obj.age = this.age;
43             return obj;
44         }
45     }

好,下麵運行一下,果然達到了我們預期的效果。

這兩種克隆的方式分別叫做 淺複製,深複製。

其實,在一些特定的場合,我們會經常用到,例如: DataSet 中,有Clone 和 Copy 兩個方法,一個是淺複製,一個是深複製。

好了,說了這麼多了,小伙伴們好好體會一下吧!

幾天就講到這了,下一篇會講 模板方法模式


本系列將持續更新,喜歡的小伙伴可以點一下關註和推薦,謝謝大家的支持。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • asp.net core + mysql + ef core + linux 以前開髮網站是針對windows平臺,在iis上部署。由於這次需求的目標伺服器是linux系統,就嘗試用跨平臺的.NET core來開發和部署。結果還是比較滿意,整個過程如下,歡迎交流: 開發環境: Win10 Vs201 ...
  • 上一篇, 出現了一個至關重要的類:MvcHandler, 接下來就來看一下MvcHandler吧. 先不看具體方法, 先看一下類裡面的情況. 從上面看, 有兩種執行方式, 一種是同步的, 一種是非同步的. 那預設情況下, 其實會走非同步的方式. 但是這裡呢, 我想用同步的方式去分析, 其實過程原理都是一 ...
  • 當源類型與目標類型不是基元類型時CLR便不能自己進行編譯轉換。 下例為Rational(有理數類型)與string,int的轉化。 轉換操作符是將對象從一個類型轉化成另一個類型的方法。可以使用特殊語法來定義裝換操作符方法。 CLR要求轉換操作符的重載方法必須是public 和static方法。c#要 ...
  • 轉載自: http://blog.csdn.net/xmxkf/article/details/51454685 本文出自:【openXu的博客】 ...
  • 單元測試的核心就是:只測試眼前的邏輯。這就要求所有的依賴項都要使用仿類來代替,也就是所謂的 Mock Object。在測試 和 的時候,我遇到了需要對 和 進行 Mock 的需求。因為這兩個組件相互依賴,還依賴別的組件,我折騰了好一陣才搞定這個問題。具體的方法分兩種:直接使用 Moq 進行 Mock ...
  • 在頁面想webApi post json數據的時候,發現webapi不能直接以json的方式接受數據(註:我是沒有發現一個很好的方式來post json數據的);但是可以以數據結構的方式傳遞; 如下: 1 public class Diff 2 { 3 public string Id { set; ...
  • 預設MVC的 View頁面 不參與編譯,當更改view對應model後,view編譯也能通過,或者頁面有錯誤的服務端代碼時也不會報錯。 那麼如何在編譯的時候能讓View中的錯誤也不能通過呢。經過查找找到了方法,本機MVC5.0 適用,其他版本未試。 方法: 一、修改 .csproj 工程文件,用tx ...
  • 1.添加編輯按鈕 打開文件Index.js 【..\MyCompanyName.AbpZeroTemplate.Web\Areas\Mpa\Views\Category\Index.js】 在actions中添加如下代碼: actions: { title: app.localize('Action ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...