1. 程式人生 > 其它 >設計模式—建立型(單例模式、工廠模式、建立者模式、原型模式)

設計模式—建立型(單例模式、工廠模式、建立者模式、原型模式)

建立型

概述:

  1. 建立型模式對類的例項化過程進行了抽象
  2. 將軟體模組中物件的建立和物件的使用分離
  3. 暴露建立介面,遮蔽建立細節,讓關於自身的邏輯內聚在內部,符合單一職責原則

要素

  • 建立什麼(What)
  • 由誰建立(Who)
  • 何時建立(when)

建立型模式主要從這三個方面考慮,為程式設計提供更大的靈活性。

建立型模式隱藏了類的例項的建立細節,通過隱藏物件如何被建立和組合在一起達到使整個系統獨立的目的。

簡單工廠模式(Simple Factory)

定義

  • 又稱為靜態工廠方法(Static Factory Method)模式,它屬於類建立型模式。
  • 在簡單工廠模式中,可以根據引數的不同返回不同類的例項。
  • 簡單工廠模式專門定義一個工廠類負責建立其他類的例項,被建立的例項通常都具有共同的父類

最大優點:將物件的建立與物件的使用分離

最大缺點:不夠靈活,有新的具體產品新增或增加時,需要修改原有邏輯程式碼

結構

  • 工廠角色——負責實現建立所有例項的內部邏輯;
  • 抽象產品角色——所建立的所有物件的父類,負責描述所有例項所共有的公共介面;
  • 具體產品角色——建立目標,所有建立的物件都充當這個角色的某個具體類的例項。

例項

Demo1

場景1:

  1. 一個代工廠可以提供多個品牌電視的生產線(如海爾、TCL等)
  2. 呼叫者(Prgram)不需要知道這些生產出的電視的名稱,只需要知道表示該電視類的一個品牌引數及建立方法
  3. 把該引數傳入方法即可返回一個相應的按鈕物件

公共父類

   public interface TV
    {
         //播放
         void play();
    }

子類

    public class HairTV : TV
    {
        public void play()
        {
            Console.WriteLine("海爾電視正在播。。。。"); 
        }
    }
    public class TCLTV : TV
    {
        public void play()
        {
            Console.WriteLine("TCLTV電視正在播。。。。"); 
        }
    }

工廠:

  public  class TVFactoty
    {
        public static TV GetTV(string Brand)
        {
            if (Brand.Equals("Hair"))
            {
                Console.WriteLine("生產一臺海爾電視");
                return new HairTV();
            }else if (Brand.Equals("TCL"))
            {
                Console.WriteLine("生產一臺TCL電視");
                return new HairTV();
            }
            else
            {
                throw new Exception($"沒有對應{Brand}品牌的生產線");
            }
        }
    }

呼叫:

TV tv = TVFactoty.GetTV("Hair");
tv.play();

Demo2

許可權管理
在某OA系統中,系統根據對比使用者在登入時輸入的賬號和密碼以及在資料庫中儲存的賬號和密碼是否一致來進行身份驗證,

如果驗證通過,則取出儲存在資料庫中的使用者許可權等級(以整數形式儲存),根據不同的許可權等級建立不同等級的使用者物件,不同等級的使用者物件擁有不同的操作許可權。

現使用簡單工廠模式來設計該許可權管理模組

父類:

   public abstract class User
    {
        //不同角色的公共方法
        public virtual void sameOperation()
        {
            Console.WriteLine("修改個人休息");
        }
        //子類不同的操作許可權的方法
        public abstract void diffOperation();
    }

子類:

    public class Employee : User
    {
        public override void diffOperation()
        {
            Console.WriteLine("僱員只有請假許可權"); ;
        }
    }
    public class Manager : User
    {
        public override void diffOperation()
        {
            Console.WriteLine("主管有請假,審批"); 
        }
    }
    public class Administrator : User
    {
        public override void diffOperation()
        {
            Console.WriteLine("管理員擁有建立和管理假條許可權"); 
        }
    }

身份驗證類:

   public class IdentifyHelper
    {
        public static int IdentifyPermission(string name,string pwd)
        {
            if (name.Equals("zs")&& pwd.Equals("123"))
            {
                return 1;
            }else if (name.Equals("rookie") && pwd.Equals("456"))
            {
                return 2;
            }
            else
            {
                return 0;
            }
        }
    }

工廠類:

   public  class UserFactory
    {
		public static User getUser(int permission)
		{
			if (0 == permission)
			{
				return new Employee();
			}
			else if (1 == permission)
			{
				return new Manager();
			}
			else if (2 == permission)
			{
				return new Administrator();
			}
			else
			{
				return null;
			}
		}
	}

呼叫:

 User user;
 int level = IdentifyHelper.IdentifyPermission("zs", "123");
 user = UserFactory.getUser(level);
 user.sameOperation();
 user.diffOperation();

結果:

修改個人休息
主管有請假,審批

優缺點

缺點

  • 工廠類集中了所有產品建立邏輯,又涉及經常改動,系統風險變高
  • 工廠裡的物件建立原來是在類裡,工程類新增了類的個數,增加了系統複雜度
  • 系統拓展困難,一旦有新的產品,就得增加ifelse的邏輯,產品過多後,邏輯就會過於複雜不利於拓展和維護
  • 採用了靜態工廠,造成了工廠角色無法形成繼承的等級的結構

優點

  • 實現了對責任的分割,它提供了專門的工廠類用於建立物件。
  • 對客戶端遮蔽了產品類的建立細節,提供了只需少量引數的建立介面
  • 可以引入配置檔案,在不修改客戶端程式碼的情況下更換或增加新的具體產品類

適用場景

  • 工廠類裡的產品型別比較少
  • 客戶端不關注建立物件細節,只想簡化獲取物件的過程(採用儘可能少的引數)

工廠方法模式(Factory Method)

前情提要:

簡單工廠模式有以下缺點

  • 所有產品類的建立都封裝在一個工廠類,產品類與工廠類的耦合度高,影響了系統靈活性和拓展性
  • 有新的產品型別加入時,需要修改原有的型別建立邏輯,違背了”開放—封閉原則“

思考:我們能不能將產品類的建立不封裝到一個工廠類,只要產品類的建立分散到不同的工廠類,有新的產品型別時,我們可以建立對應新的工廠類,不用改變原有類的工廠類。變修改為新增,更符合”開放—封閉原則“

模式結構

定義

  • 又稱為工廠模式,也叫虛擬構造器(Virtual Constructor)模式、多型工廠(Polymorphic Factory)模式
  • 工廠方法模式是簡單工廠模式的進一步抽象和推廣,使用了面向物件的多型性,保持了簡單工廠模式的優點,而且克服了它的缺點
  • 將產品類的例項化操作延遲到工廠子類中完成,即通過工廠子類來確定究竟應該例項化哪一個具體產品類
  • 工廠方法模式可以允許系統在不修改原有工廠角色的情況下引進新產品

結構

  • 抽象工廠角色——負責定義建立產品物件的公共介面;
  • 具體工廠角色——負責實現生成具體的產品物件的方法
  • 抽象產品角色——所建立的所有物件的父類,負責描述所有例項所共有的公共介面;
  • 具體產品角色——建立目標,所有建立的物件都充當這個角色的某個具體類的例項。

例項

Demo1

抽象產品類

   public interface TV
    {
         void play();
    }

產品類:

    public class HairTV : TV
    {
        public void play()
        {
            Console.WriteLine("海爾電視正在播。。。。"); 
        }
    }
    public class TCLTV : TV
    {
        public void play()
        {
            Console.WriteLine("TCLTV電視正在播。。。。"); 
        }
    }

抽象工廠類

  public interface  TVFactoty
  {
        public TV GetTV();
  }

工廠類

    public class HairTVFactory: TVFactoty
    {
        public TV GetTV()
        {
            Console.WriteLine("海爾電視生產一臺");
            return new HairTV();
        }
    }
    public class TCLTVFactory : TVFactoty
    {
        public TV GetTV()
        {
            Console.WriteLine("TCL電視生產一臺");
            return new TCLTV();
        }
    }

測試

            TV tv1 = new HairTVFactory().GetTV();
            tv1.play();
            TV tv2 = new TCLTVFactory().GetTV();
            tv2.play();

結果列印

海爾電視生產一臺
海爾電視正在播。。。。
TCL電視生產一臺
TCLTV電視正在播。。。。

Demo2

( 4、工廠方法模式及擴充套件)

優缺點

優點

  • 使用者只需要關心所需產品對應的工廠,無須關心建立細節,甚至無須知道具體產品類的類名(因為指定了建立的具體工廠,不用通過傳參去確定產品型別)。
  • 工廠可以自主確定建立何種產品物件,而如何建立這個物件的細節則完全封裝在具體工廠內部
  • 系統中加入新產品時,無須修改抽象工廠和抽象產品提供的介面,無須修改客戶端,也無須修改其他的具體工廠和具體產品;只要新增一個具體工廠和具體產品就可以了,更符合”開放——封閉原則“

缺點

  • 有新的產品類時,需要建立與之對應的新的具體工廠類。系統的中類的個數成對增加,增加了系統的複雜度
  • 引入了抽象工廠的概念,增加了系統的抽象性和理解難度
  • 在實現時可能需要用到DOM、反射等技術,增加了系統的實現難度。

適用情況

  • 一個類不知道它所需要的物件的類;
  • 一個類通過其子類來指定建立哪個物件;
  • 將建立物件的任務委託給多個工廠子類中的某一個,客戶端在使用時可以無須關心是哪一個工廠子類建立產品子類,需要時再動態指定。

模式擴充套件

  • 使用多個工廠方法:在抽象工廠角色中可以定義多個工廠方法,從而使具體工廠角色實現這些不同的工廠方法,這些方法可以包含不同的業務邏輯,以滿足對不同的產品物件的需求。
  • 產品物件的重複使用:工廠物件將已經建立過的產品儲存到一個集合(如陣列、List等)中,然後根據客戶對產品的請求,對集合進行查詢。如果有滿足要求的產品物件,就直接將該產品返回客戶端;如果集合中沒有這樣的產品物件,那麼就建立一個新的滿足要求的產品物件,然後將這個物件在增加到集合中,再返回給客戶端。
  • 多型性的喪失和模式的退化:如果工廠僅僅返回一個具體產品物件,便違背了工廠方法的用意,發生退化,此時就不再是工廠方法模式了。一般來說,工廠物件應當有一個抽象的父型別,如果工廠等級結構中只有一個具體工廠類的話,抽象工廠就可以省略,也將發生了退化。當只有一個具體工廠,在具體工廠中可以建立所有的產品物件,並且工廠方法設計為靜態方法時,工廠方法模式就退化成簡單工廠模式。

抽象工廠模式(Abstract Factory)

背景:在工廠方法模式裡已經對簡易工廠模式進行了升級,但是同樣引入了一個新的問題,新建立一個產品就需要引入一個具體的工廠類,這個工廠類裡只有一個建立該產品的方法。這引發了下面的思考

  • 一個工廠裡只生產一種產品嗎?這是不是很大的浪費
  • 能不能做到這個工廠還能生產多個其他產品呢?

基礎概念(產品等級結構、產品族)

  • 產品等級結構:產品等級結構即產品的繼承結構。(電視——海爾電視、冰箱——海爾冰箱)
  • 產品族:產品族是指由同一個工廠生產的,位於不同產品等級結構中的一組產品。(海爾電視,海爾冰箱,海爾洗衣機)

具體例子

  • Button、Text是兩個產品結構

  • Linux、Windows、Unix是不同產品族

動機

  • 多個產品,位於不同產品等級結構中,不同產品族中
  • 在原有工廠方法基礎再次抽象,擴大了產品的種類範圍

與工廠方法模式的區分

  • 工廠方法——一個產品族,多個等級結構的產品
  • 抽象工廠方法——多個產品族,多個等級結構的產品(在產品種類上有拓展)

結構

  • 抽象工廠——用於宣告生成各個產品等級結構的抽象產品(一個產品族裡的抽象產品)的建立方法的介面
  • 具體工廠——實現了抽象工廠宣告的生成抽象產品的方法,這些產品構成了一個產品族
  • 抽象產品——每種產品宣告介面,在抽象產品中定義了產品的抽象業務方法
  • 具體產品——具體工廠生產的具體產品物件,實現抽象產品介面中定義的業務方法。

例項

抽象產品

   public interface TV
    {
         void play();
    }
    public interface Fridge
    {
        void CreateIce();
    }

具體產品

//"海爾"族類產品
    public class HairTV : TV
    {
        public void play()
        {
            Console.WriteLine("海爾電視正在播。。。。"); 
        }
    }
        public class HairFridge : Fridge
    {
        public void CreateIce()
        {
            Console.WriteLine("海爾冰箱開始製冰"); ;
        }
    }
//"TCL"族類產品
    public class TCLTV : TV
    {
        public void play()
        {
            Console.WriteLine("TCLTV電視正在播。。。。"); 
        }
    }
    public class TCLFridge : Fridge
    {
        public void CreateIce()
        {
            Console.WriteLine("TCL冰箱開始製冰"); 
        }
    }

抽象工廠

  public  interface Factoty
  {
        //生產電視
        public  TV GetTV() ;
        //生產冰箱
        public Fridge GetFridge();
  }

優缺點

優點
  • 可以通過具體工廠類建立一個產品族中的多個物件,增加或者替換產品族比較方便,增加新的具體工廠和產品族很方便;
缺點
  • 增加新的產品等級結構很複雜,需要修改抽象工廠所有的具體工廠類,對“開閉原則”支援性降低

單例模式(Singleton)

背景

對於系統中的某些類,我們希望一次建立。讓它常駐在記憶體裡,我們後續使用時呼叫,避免重複建立物件的操作,思路排除:

  • 定義一個全域性變數可以確保物件隨時都可以被訪問,但不能防止我們例項化多個物件(不行)
  • 讓類自身負責儲存它的唯一例項(可行),這個類可以保證沒有其他例項被建立,並且它可以提供一個訪問該例項的方法。這就是單例模式的模式動機。

定義

  • 單例模式確保某一個類只有一個例項,而且自行例項化並向整個系統提供這個例項,這個類稱為單例類,它提供全域性訪問的方法。

規則

  • 一個類只有一個例項
  • 這個類必須自行建立這個例項
  • 類必須自行向整個系統提供這個例項的獲取方法

結構

單例類,內部成員

  • 靜態私有成員變數
  • 私有建構函式,確保外部無法通過new關鍵字去建立例項
  • 靜態公有的工廠方法,方法需要以下幾個功能
    1. 檢驗例項是否為空,為空則建立例項,儲存在靜態私有變數裡(確保唯一)
    2. 返回例項化物件

常見模式

  • 餓漢式單例——在自己被載入時就將自己例項化

    單從資源利用效率角度來講,這個比懶漢式單例類稍差些。從速度和反應時間角度來講,則比懶漢式單例類稍好些。

  • 懶漢式單例——在被呼叫時,按需生成或返回例項

    必須處理好在多個執行緒同時首次引用此類時的訪問限制問題我想跳轉

例項

單執行緒餓漢模式
    public class FullPatternSingleton
    {
        //靜態類變數只會例項化一次
        private static FullPatternSingleton instance = new FullPatternSingleton();
        private FullPatternSingleton()
        {

        }
        public static FullPatternSingleton GetInstance()
        {
            return instance;
        }
        
    }
單執行緒餓漢模式(利用靜態構造方法建立例項)
    public class FullPatternSingleton2
    {
        private static FullPatternSingleton2 singleton2 = null;
        /// <summary>
        /// 建構函式
        /// </summary>
        private FullPatternSingleton2()
        {
          
        }

        /// <summary>
        /// 採用靜態建構函式,在類被載入時就會被執行,且僅僅執行一次,所以可以用於構造例項的
        /// </summary>
        static FullPatternSingleton2()
        {
            singleton2 = new FullPatternSingleton2();
        }
        public static FullPatternSingleton2 GetInstance()
        {
            return singleton2;
        }
    }
單執行緒懶漢模式
 public class HungrySingletonPattern
    {
        //靜態私有變數
        private static HungrySingletonPattern  singletonInstance= null;
        //私有構造方法
        private  HungrySingletonPattern()
        {
            
        }
        //暴露給外界獲取例項的方法
        public static HungrySingletonPattern GetInstance()
        {
            if (singletonInstance == null)
            {
                singletonInstance = new HungrySingletonPattern();
            }
            return singletonInstance;
        }
        public void dosomething()
        {
            Console.WriteLine("單例類的public方法");
        }
    }
多執行緒餓漢模式
    public class SingletonInMultiThread
    {
        private static SingletonInMultiThread instance = new SingletonInMultiThread();
        public string Name { get; set; }
        private SingletonInMultiThread()
        {
            //this.Name = name;
        }
        public static Object ObjLock = new object();
        public static SingletonInMultiThread GetInstance() {
            return instance;
            //lock (ObjLock)
            //{
            //    return instance;
            //}
            
        }
    }
多執行緒懶漢模式
   public class SingletonInMultiThread2
    {
        private static SingletonInMultiThread2 instance ;
        private SingletonInMultiThread2()
        {
            
        }
        public static Object ObjLock = new object();
        public static SingletonInMultiThread2 GetInstance() {
            if (instance == null)
            {
                //kkk
                lock (ObjLock)
                {
                    //這裡還要加一層判斷,因為在多執行緒併發的情況下,會出現以下場景:
                    //多個執行緒可能都執行到kkk的位置,然後在kkk位置排隊等待鎖釋放
                    //獲取鎖的第一個執行緒會建立一個例項後釋放鎖
                    //等待在kkk位置的第二個執行緒,拿到鎖,又會建立一個新的例項
                    //所以要規避這種情況,需要在鎖的內部建立例項時再加一層例項是否為空的判斷
                    if (instance == null)
                    {
                        instance = new SingletonInMultiThread2();
                    }
                    //有同學好奇,為什麼要加兩層例項為空的判斷
                    //其實最外層例項是否為空是基於效能考慮, 如果判斷不為空,就不會進入排隊等待鎖的邏輯;
                    //如果不加這層判斷,每個執行緒都會參與鎖的競爭,對程式效能不力,但是仍可以保證只產生一個例項
                    //第二層在鎖的內部做的例項是否為空的判斷是必要的,不加無法保證唯一例項

                }
            }
            return instance;
        }
    }

優缺點

  • 優點——提供了對唯一例項的受控訪問並可以節約系統資源
  • 缺點——因為缺少抽象層而難以擴充套件,且單例類職責過重。

適用於系統只需要一個例項物件;客戶呼叫類的單個例項只允許使用一個公共訪問點

建造者模式

背景

  • 現實世界中還是在軟體系統中,都存在一些複雜的物件,它們擁有多個組成部分(比如計算機:由主機板、cpu、顯示卡、電源等組成)
  • 對於使用者而言,他們不需要了解產品的組裝細節,只需要一臺完整的組裝好的產品
  • 在軟體開發中,也存在大量類似汽車一樣的複雜物件,它們擁有一系列成員屬性,這些成員屬性中有些是引用型別的成員物件
  • 複雜物件相當於一輛有待建造的汽車,而物件的屬性相當於汽車的部件,建造產品的過程就相當於組合部件的過程。由於組合部件的過程很複雜,因此,這些部件的組合過程往往被“內聚化”到一個稱作建造者的物件裡,建造者返還給客戶端的是一個已經建造完畢的完整產品物件,而使用者無須關心該物件所包含的屬性以及它們的組裝方式

建造者模式可以將部件和其組裝過程分開,在對客戶端遮蔽情況下一步一步建立物件並且返回

給使用者。

定義

  • 將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。
  • 建造者模式是一步一步建立一個複雜的物件,它允許使用者只通過指定複雜物件的型別和內容就可以構建它們

模式結構

Builder——抽象建造者(概念生產工廠,定義產品的建立方法和返回方法)
ConcreteBuilder——具體建造者(實際生產工廠)
Director——指揮者(制定生產工序,先做零件A,再做零件B,再組裝。。。。):一方面它隔離了客戶與生產過程;另一方面它負責控制產品的生成過程
Product——產品角色(產品)

Demo

產品Product定義:定義一個套餐類Meal,套餐由主食和飲料組成

   public class Meal
    {
        //假設套餐由主食和飲料組成
        public string drink { get; set; }
        public string food { get; set; }
    }

抽象建造者Builder

   //套餐建立抽象類
    public abstract class MealBuilder
    {
        protected Meal meal = new Meal();
        public abstract void setFood();
        public abstract void setDrink();

        public Meal GetMeal()
        {
            return meal;
        }

    }

定義兩個具體的建造者,KFCBuilder(肯德基店)、MacDonaldBuilder(麥當勞店),模擬每個店的套餐不一樣

    //KFC
    public class KFCBuilder : MealBuilder
    {
        public override void setFood()
        {
            //這裡可以放食物的建立過程
            meal.food = "麥辣雞腿堡";
        }

        public override void setDrink()
        {
            //這裡可以放飲料的詳細製作過程
            meal.drink = "百事可樂";
        }
    }
    //MacDonald
    public class MacDonaldBuilder : MealBuilder
    {
        public override void setFood()
        {
            //這裡可以放食物的建立過程
            meal.food = "巨無霸雙層堡";
        }

        public override void setDrink()
        {
            //這裡可以放飲料的詳細製作過程
            meal.drink = "可口可樂";
        }
    }

定義一個指揮者,在例子裡就是店員角色,她負責套餐的製作及為使用者提供做好的套餐

    public class Waiter
    {
        private MealBuilder _MealBuilder;
        public Waiter(MealBuilder mealBuilder)
        {
            this._MealBuilder = mealBuilder;
        }
        public  Meal GetMeal()
        {
            _MealBuilder.setFood();
            _MealBuilder.setDrink();
            return _MealBuilder.GetMeal();
        }
    }

實際使用者呼叫:客戶無需關心生產細節,只需確定具體建造者的型別即可

            //使用者指定店鋪,這裡使用者選的是肯德基的店
            MealBuilder builder = new KFCBuilder();
            //使用者找的店員自然是肯德基的店員
            Waiter waiter = new Waiter(builder);
            //使用者只需要從店員那裡取餐就行了,他不需要知道套餐是咋做的
            Meal meal = waiter.GetMeal();
            Console.WriteLine($"當前套餐的食物是{meal.food},飲料是{meal.drink}");

輸出:當前套餐的食物是麥辣雞腿堡,飲料是百事可樂

優缺點

優點
  • 客戶端不必知道產品內部組成的細節,將產品本身與產品的建立過程解耦,使得相同的建立過程可以建立不同的產品物件。
  • 每一個具體建造者都相對獨立,而與其他的具體建造者無關,因此可以很方便地替換具體建造者或增加新的具體建造者,使用者使用不同的具體建造者即可得到不同的產品物件
  • 可以更加精細地控制產品的建立過程。將複雜產品的建立步驟分解在不同的方法中,使得建立過程更加清晰,也更方便使用程式來控制建立過程。
  • 增加新的具體建造者無須修改原有類庫的程式碼,指揮者類針對抽象建造者類程式設計,系統擴充套件方便,符合“開閉原則”。
缺點
  • 建造者模式所建立的產品一般具有較多的共同點,其組成部分相似,如果產品之間的差異性很大,則不適合使用建造者模式,因此其使用範圍受到一定的限制。
  • 如果產品的內部變化複雜,可能會導致需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大。

適用場景

  • 產品物件通常包含多個成員屬性,有複雜的內部結構。但是屬性比較固定
  • 需要生成的產品物件的屬性相互依賴,需要指定其生成順序
  • 物件的建立過程在不在物件所在類中定義,定義在指揮者類裡。
  • 隔離複雜物件的建立和使用,並使得相同的建立過程可以通過不同的建立物件建立不同的產品。

常見例子

  1. 郵件的構建(發件人、接收人、抄送人、標題、內容、附件)
  2. 遊戲人物裝扮

建造者模式的簡化

  • 省略抽象建造者角色:如果系統中只需要一個具體建造者的話,可以省略掉抽象建造者。
  • 省略指揮者角色:在具體建造者只有一個的情況下,如果抽象建造者角色已經被省略掉,那麼還可以省略指揮者角色,讓Builder角色扮演指揮者與建造者雙重角色。

建造者模式與抽象工廠模式的比較

  • 建造者模式返回一個組裝好的完整產品

    抽象工廠模式返回一系列相關的產品,這些產品位於不同的產品等級結構,構成了一個產品族。

  • 在抽象工廠模式中,客戶端例項化工廠類,然後呼叫工廠方法獲取所需產品物件,

    建造者模式中,客戶端可以不直接呼叫建造者的相關方法,而是通過指揮者類來指導如何生成物件,包括物件的組裝過程和建造步驟,它側重於一步步構造一個複雜物件,返回一個完整的物件。

  • 如果將抽象工廠模式看成汽車配件生產工廠,生產一個產品族的產品(海信電視、TCL電視)

    那麼建造者模式就是一個汽車組裝工廠,通過對部件的組裝可以返回一輛完整的汽車

原型模式(Prototype Pattern)

C# 設計模式-原型模式 - shine聲 - 部落格園 (cnblogs.com)

動機

在軟體系統中,有些物件的建立過程較為複雜,而且有時候需要頻繁建立。

使用原型模式可以快速複製一個物件自身,克隆出多個與原型物件一模一樣的物件。

定義

原型模式——用原型例項指定建立物件的種類,並且通過複製這些原型建立新的物件。

可以直接複製一個現有的物件,而不需要重新new操作去建立。這種建立分兩種方式,淺拷貝和深拷貝

模式結構

Prototype——抽象原型類
ConcretePrototype——具體原型類
Client——客戶類

模式建立

  • ICloneable是系統自帶的介面,來定義需要用clone方法的。
  • 只要實現ICloneable介面,都可以通過重寫clone方法去實現複製方法
    public class Person:ICloneable
    {
        public int Age { get; set; }
        public string Name { get; set; }
        
        public object Clone()
        {
           //淺拷貝
           return this.MemberwiseClone();
        }
    }
淺拷貝
this.MemberwiseClone();
深拷貝
  • 序列化和反序列化建立了一個新的物件,這樣拷貝出來的類指向就不會和原有的類一樣。
  • 建立一個
    /// <summary>
    /// 深拷貝1(通過序列化)
    /// </summary>
    /// <returns></returns>
    public object Clone()
    {
        XmlSerializer serializer = new XmlSerializer(typeof(Person));
        using (var stream = new System.IO.MemoryStream())
        {
            serializer.Serialize(stream, this);
            stream.Seek(0, System.IO.SeekOrigin.Begin);
            return serializer.Deserialize(stream) as Person;
        }
    }
        /// <summary>
        /// 通過轉化成json序列化
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static T CloneJson<T>(this T source)
        {
            if (Object.ReferenceEquals(source, null))
            {
                return default(T);
            }

            var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };
            return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), deserializeSettings);
        }

優缺點

優點:

  1.隱藏了建立例項的繁瑣過程,只需要通過clone方法就能獲取例項。

  2.使用拷貝代替new,減少資源損耗。

缺點:

  需要在每個需要拷貝的類實現中實現clone方法。