原型模式(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
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...