最近重溫了一下星爺的《唐伯虎點秋香》,依然讓我捧腹不已,幻想着要是我也能有一名秋香如此的侍女,夫復何求呀,帶着這個美好的幻想沉沉睡去...git
忽然想到,我是一名程序猿呀,想要什麼對象不是易如反掌嗎,New一個唄,不光是秋香,春、夏、冬都要,身材要超A的,百度好三圍(82, 54, 86),開幹...app
Beauty類,包含美人的屬性函數
public class Beauty { public Beauty(int bust, int theWaist, int hipline) { Bust = bust; TheWaist = theWaist; Hipline = hipline; //模擬產生一名美人的時長 Thread.Sleep(3000); } /// <summary> /// 名稱 /// </summary> public string Name { get; set; } /// <summary> /// 才藝 /// </summary> public string Talent { get; set; } /// <summary> /// 胸圍 /// </summary> public int Bust { get; set; } /// <summary> /// 腰圍 /// </summary> public int TheWaist { get; set; } /// <summary> /// 臀圍 /// </summary> public int Hipline { get; set; } /// <summary> /// 起名 /// </summary> /// <param name="name"></param> public void SetName(string name) { Name = name; } /// <summary> /// 設置才藝 /// </summary> /// <param name="talent"></param> public void SetTalent(string talent) { Talent = talent; } }
客戶端生產美女性能
internal class Program { private static void Main(string[] args) { var sw = new Stopwatch(); sw.Start(); var beauty1 = new Beauty(82, 54, 86); sw.Stop(); Console.WriteLine($"生產第一名美人耗時:{sw.ElapsedMilliseconds}/ms\n"); beauty1.SetName("秋香"); beauty1.SetTalent("彈琴"); sw.Restart(); var beauty2 = new Beauty(82, 54, 86); sw.Stop(); Console.WriteLine($"生產第二名美人耗時:{sw.ElapsedMilliseconds}/ms\n"); beauty2.SetName("春香"); beauty2.SetTalent("畫畫"); sw.Restart(); var beauty3 = new Beauty(82, 54, 86); sw.Stop(); Console.WriteLine($"生產第三名美人耗時:{sw.ElapsedMilliseconds}/ms\n"); beauty3.SetName("夏香"); beauty3.SetTalent("舞蹈"); Show(beauty1); Show(beauty2); Show(beauty3); Console.WriteLine("\nHappy Ending~"); Console.ReadLine(); } public static void Show(Beauty beauty) { Console.WriteLine($"我是 {beauty.Name},身材[{beauty.Bust}-{beauty.TheWaist}-{beauty.Hipline}],個人才藝是 {beauty.Talent}"); } }
結果展現:this
生產第一名美女耗時:3008/ms 生產第二名美女耗時:3001/ms 生產第三名美女耗時:3000/ms 我是 秋香,身材[82-54-86],個人才藝是 彈琴 我是 春香,身材[82-54-86],個人才藝是 畫畫 我是 夏香,身材[82-54-86],個人才藝是 舞蹈
個人美人產生了,但就是每次都是經過New建立,設置的標準身材(82-54-86)不變但每次都要設置,並且每次耗時都很長,要是再生產更多,豈不勞累又耗時...pwa
正在苦惱之時,忽然靈感乍現,可使用原型模式(Prototype)解決呀code
原型模式 用原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象對象
其實就是從一個對象再建立另外一個可定製的對象,並且不須要知道任何建立的細節接口
.NET在System命名空間中提供了ICloneable接口,其中只有一個方法Clone(),實現這個接口就能夠完成原型模式了。ip
開始改造:
Beauty實現ICloneable接口
public class Beauty : ICloneable { public Beauty(int bust, int theWaist, int hipline) { Bust = bust; TheWaist = theWaist; Hipline = hipline; //模擬產生一名美人的時長 Thread.Sleep(3000); } /// <summary> /// 名稱 /// </summary> public string Name { get; set; } /// <summary> /// 才藝 /// </summary> public string Talent { get; set; } /// <summary> /// 胸圍 /// </summary> public int Bust { get; set; } /// <summary> /// 腰圍 /// </summary> public int TheWaist { get; set; } /// <summary> /// 臀圍 /// </summary> public int Hipline { get; set; } /// <summary> /// 起名 /// </summary> /// <param name="name"></param> public void SetName(string name) { Name = name; } /// <summary> /// 設置才藝 /// </summary> /// <param name="talent"></param> public void SetTalent(string talent) { Talent = talent; } public object Clone() { return this.MemberwiseClone(); } }
客戶端生產時,除第一個調用new()外,其餘使用Clone()方法建立
internal class Program { private static void Main(string[] args) { var sw = new Stopwatch(); sw.Start(); var beauty1 = new Beauty(82, 54, 86); sw.Stop(); Console.WriteLine($"生產第一名美女耗時:{sw.ElapsedMilliseconds}/ms\n"); beauty1.SetName("秋香"); beauty1.SetTalent("彈琴"); sw.Restart(); var beauty2 = (Beauty)beauty1.Clone(); sw.Stop(); Console.WriteLine($"生產第二名美女耗時:{sw.ElapsedMilliseconds}/ms\n"); beauty2.SetName("春香"); beauty2.SetTalent("畫畫"); sw.Restart(); var beauty3 = (Beauty)beauty1.Clone(); sw.Stop(); Console.WriteLine($"生產第三名美女耗時:{sw.ElapsedMilliseconds}/ms\n"); beauty3.SetName("夏香"); beauty3.SetTalent("舞蹈"); Show(beauty1); Show(beauty2); Show(beauty3); Console.WriteLine("\nHappy Ending~"); Console.ReadLine(); } public static void Show(Beauty beauty) { Console.WriteLine($"我是 {beauty.Name},身材[{beauty.Bust}-{beauty.TheWaist}-{beauty.Hipline}],個人才藝是 {beauty.Talent}"); } }
結果展現:
生產第一名美女耗時:3009/ms 生產第二名美女耗時:0/ms 生產第三名美女耗時:0/ms 我是 秋香,身材[82-54-86],個人才藝是 彈琴 我是 春香,身材[82-54-86],個人才藝是 畫畫 我是 夏香,身材[82-54-86],個人才藝是 舞蹈
咱們能夠看到,除了第一個建立耗時之外,其餘Clone出來的對象基本不耗時,並且不用重複設置固定屬性(三圍)(通常在初始化的信息不發生變化的狀況下,克隆是最好的方法,既隱藏了對象建立的細節,又對性能大大的提升),我心甚歡...
因而想着對美人的才藝進行升級
Talent類,對才藝描述,增長才藝段位
public class Talent { /// <summary> /// 才藝名 /// </summary> public string Name { get; set; } /// <summary> /// 段位 /// </summary> public int Level { get; set; }
Beauty類改造
public class Beauty : ICloneable { public Beauty(int bust, int theWaist, int hipline) { Bust = bust; TheWaist = theWaist; Hipline = hipline; Talent = new Talent(); //模擬產生一名美人的時長 Thread.Sleep(3000); } /// <summary> /// 名稱 /// </summary> public string Name { get; set; } /// <summary> /// 才藝 /// </summary> public Talent Talent { get; set; } /// <summary> /// 胸圍 /// </summary> public int Bust { get; set; } /// <summary> /// 腰圍 /// </summary> public int TheWaist { get; set; } /// <summary> /// 臀圍 /// </summary> public int Hipline { get; set; } /// <summary> /// 起名 /// </summary> /// <param name="name"></param> public void SetName(string name) { Name = name; } /// <summary> /// 設置才藝 /// </summary> /// <param name="talent"></param> public void SetTalent(string name, int level) { Talent.Name = name; Talent.Level = level; } public object Clone() { return this.MemberwiseClone(); } }
客戶端設置才藝段位:
internal class Program { private static void Main(string[] args) { var sw = new Stopwatch(); sw.Start(); var beauty1 = new Beauty(82, 54, 86); sw.Stop(); Console.WriteLine($"生產第一名美女耗時:{sw.ElapsedMilliseconds}/ms\n"); beauty1.SetName("秋香"); beauty1.SetTalent("彈琴", 10); sw.Restart(); var beauty2 = (Beauty)beauty1.Clone(); sw.Stop(); Console.WriteLine($"生產第二名美女耗時:{sw.ElapsedMilliseconds}/ms\n"); beauty2.SetName("春香"); beauty2.SetTalent("畫畫", 9); sw.Restart(); var beauty3 = (Beauty)beauty1.Clone(); sw.Stop(); Console.WriteLine($"生產第三名美女耗時:{sw.ElapsedMilliseconds}/ms\n"); beauty3.SetName("夏香"); beauty3.SetTalent("舞蹈", 8); Show(beauty1); Show(beauty2); Show(beauty3); Console.WriteLine("\nHappy Ending~"); Console.ReadLine(); } public static void Show(Beauty beauty) { Console.WriteLine($"我是 {beauty.Name},身材[{beauty.Bust}-{beauty.TheWaist}-{beauty.Hipline}],個人才藝是 {beauty.Talent.Name} 段位 {beauty.Talent.Level}"); } }
結果展現:
生產第一名美女耗時:3022/ms 生產第二名美女耗時:0/ms 生產第三名美女耗時:0/ms 我是 秋香,身材[82-54-86],個人才藝是 舞蹈 段位 8 我是 春香,身材[82-54-86],個人才藝是 舞蹈 段位 8 我是 夏香,身材[82-54-86],個人才藝是 舞蹈 段位 8
看到結果我懵了,什麼狀況,我明明把才藝設置的不同,怎麼三個最後都是 [舞蹈 段位 8] ???
良久平息以後,才明白致使上面結果的緣由,也將是本文的重點:淺複製與深複製
淺複製:複製獲得的對象的全部變量都包含有與原來的對象相同的值,而全部的對其餘對象的引用都仍然指向原來的對象。
MemberwiseClone()方法就是淺複製,若是字段是值類型的,則對該字段執行逐位複製,若是字段是引用類型,則複製引用但不復制引用對象,所以原始對象及其複本引用同一對象。
這就是上面,三個美人的才藝最後都變成同樣的緣由,才藝Talent是一個引用類型,複製三份的是Talent的引用,都指向同一個Talent對象
深複製:把引用對象的變量指向複製過的新對象,而不是原有的被引用的對象。
解決上面的問題,咱們就要用深複製,要對Talent複製一個新對象,而不是一個引用。('美人'引用了'才藝',假如'才藝'裏還有 引用,不少層,就涉及到深複製要深刻多少層的問題,須要事先考慮好,並且要小心出現循環引用的問題,本次案例咱們就深刻到第一層就能夠了)
上代碼:
Talent類,實現ICloneable接口
public class Talent : ICloneable { /// <summary> /// 才藝名 /// </summary> public string Name { get; set; } /// <summary> /// 段位 /// </summary> public int Level { get; set; } public object Clone() { return this.MemberwiseClone(); } }
Beauty類,增長私有構造函數,以便克隆「才藝」的克隆數據
public class Beauty : ICloneable { public Beauty(int bust, int theWaist, int hipline) { Bust = bust; TheWaist = theWaist; Hipline = hipline; Talent = new Talent(); //模擬產生一名美人的時長 Thread.Sleep(3000); } private Beauty(Talent talent) { this.Talent = (Talent)talent.Clone(); } /// <summary> /// 名稱 /// </summary> public string Name { get; set; } /// <summary> /// 才藝 /// </summary> public Talent Talent { get; set; } /// <summary> /// 胸圍 /// </summary> public int Bust { get; set; } /// <summary> /// 腰圍 /// </summary> public int TheWaist { get; set; } /// <summary> /// 臀圍 /// </summary> public int Hipline { get; set; } /// <summary> /// 起名 /// </summary> /// <param name="name"></param> public void SetName(string name) { Name = name; } /// <summary> /// 設置才藝 /// </summary> /// <param name="talent"></param> public void SetTalent(string name, int level) { Talent.Name = name; Talent.Level = level; } public object Clone() { //調用私有構造方法,讓「才藝」克隆完成,而後再給這個 「美人」 對象的相關字段賦值, //最終返回一個深複製的 「美人」 對象 var beauty = new Beauty(Talent) { Bust = this.Bust, TheWaist = this.TheWaist, Hipline = this.Hipline }; return beauty; } }
客戶端通之前同樣,展現結果:
生產第一名美女耗時:3008/ms 生產第二名美女耗時:0/ms 生產第三名美女耗時:0/ms 我是 秋香,身材[82-54-86],個人才藝是 彈琴 段位 10 我是 春香,身材[82-54-86],個人才藝是 畫畫 段位 9 我是 夏香,身材[82-54-86],個人才藝是 舞蹈 段位 8
看到結果,我心甚喜。忽然屋內彩虹環繞,三位美人出如今我面前,婀娜妖嬈,不對,怎麼少了個人冬香,爬向鍵盤...
叮鈴鈴,叮鈴鈴...鬧鈴響了
握艹,我要續夢~
源碼地址: https://gitee.com/sayook/DesignMode/tree/master/Prototype