本系列將和大家分享面向對象23種設計模式中常用的幾種設計模式,本章主要簡單介紹下創建型設計模式。 ...
本章是面向對象23種設計模式系列開篇,首先我們來看下什麼是設計模式?
面向對象23種設計模式:
1、面向對象語言開發過程中,遇到的種種場景和問題,提出瞭解決方案和思路,沉澱下來就變成了設計模式。
2、解決具體問題的具體招數---套路---站在前輩的肩膀上。
3、沒有什麼設計模式是完美無缺的,一種設計模式就是解決一類問題,通常設計模式在解決一類問題的同時,還會帶來別的問題,我們設計者要做的事兒,就是要揚長避短,充分發揮長處!
設計模式可以大概分為三大類:
1、創建型設計模式:關註對象的創建。
2、結構型設計模式:關註類與類之間的關係。
3、行為型設計模式:關註對象和行為的分離。
我們要做的就是學習核心套路,這裡就不做過多的描述,如果有機會會通過具體例子再和大家分享。下麵我們正式進入本章主題。
創建型設計模式:關註對象的創建。(5個)
1、單例模式(Singleton Pattern)
單例模式:
就是限制了對象的創建,重用了對象。保證進程中,某個類只有一個實例。
即使是單例,變數也不是線程安全的,單例不是為了保證線程安全。
單例的好處就是單例,就是全局唯一的一個實例。
應對一些特殊情況,比如資料庫連接池(內置了資源) ,全局唯一號碼生成器。
單例可以避免重覆創建,但是也會常駐記憶體,除非是真的有必要,否則就不要使用單例。
1.1、單例模式經典寫法(懶漢式)
using System; using System.Threading; namespace SingletonPattern { /// <summary> /// 懶漢式單例模式(經典寫法) /// 單例類:一個構造對象很耗時耗資源類型。 /// </summary> public class Singleton { /// <summary> /// 構造函數耗時耗資源 /// </summary> private Singleton() { long lResult = 0; for (int i = 0; i < 10000000; i++) { lResult += i; } Thread.Sleep(2000); Console.WriteLine("{0}被構造一次", this.GetType().Name); } /// <summary> /// 全局唯一靜態 重用這個變數 /// volatile 促進線程安全 讓線程按順序操作 /// </summary> private static volatile Singleton _singleton = null; /// <summary> /// 引用類型對象 /// </summary> private static readonly object lockSingleton = new object(); /// <summary> /// 公開的靜態方法提供對象實例 /// </summary> /// <returns> /// </returns> public static Singleton CreateInstance() { if (_singleton == null) //_singleton已經被初始化之後,就不要進入鎖等待了 { //保證任意時刻只有一個線程進入lock範圍 //也限制了併發,尤其是_singleton已經被初始化之後,故使用了雙if來解決併發限制問題 lock (lockSingleton) { //Thread.Sleep(1000); //Console.WriteLine("等待鎖1s之後才繼續。。。"); if (_singleton == null) //保證只實例化一次 { _singleton = new Singleton(); } } } return _singleton; } public int iTotal = 0; /// <summary> /// 既然是單例,大家用的是同一個對象,用的是同一個方法,那還會併發嗎 還有線程安全問題嗎? /// 即使是單例,變數也不是線程安全的,單例不是為了保證線程安全。 /// </summary> public void Increment() { //lock (lockSingleton) //{ this.iTotal++; //} } public static void Show() { Console.WriteLine(_singleton.iTotal); } } }
使用如下:
using System; using System.Collections.Generic; using System.Threading.Tasks; namespace SingletonPattern { /// <summary> /// 為什麼要有單例設計模式? /// 構造對象耗時耗資源,很多地方都需要去new, 這個方法 其他方法 其他類 /// </summary> class Program { static void Main(string[] args) { try { { //保證進程中,某個類只有一個實例 //1 構造函數私有化 避免別人還去new //2 公開的靜態方法提供對象實例 //3 初始化一個靜態欄位用於返回 保證全局都是這一個 Singleton singleton1 = Singleton.CreateInstance(); Singleton singleton2 = Singleton.CreateInstance(); Singleton singleton3 = Singleton.CreateInstance(); Console.WriteLine(object.ReferenceEquals(singleton1, singleton2)); Console.WriteLine(object.ReferenceEquals(singleton3, singleton2)); } { List<Task> tasks = new List<Task>(); for (int i = 0; i < 10000; i++) { tasks.Add(Task.Run(() => { Singleton singleton = Singleton.CreateInstance(); singleton.Increment(); })); } Task.WaitAll(tasks.ToArray()); Singleton.Show(); //即使是單例,變數也不是線程安全的,單例不是為了保證線程安全。 //iTotal 是0 1 10000 還是其他的 //結果為:其他值,1到10000範圍內都可能 線程不安全 } } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.Read(); } } }
1.2、餓漢式寫法(靜態構造函數)
using System; using System.Threading; namespace SingletonPattern { /// <summary> /// 餓漢式 /// </summary> public class SingletonSecond { private static SingletonSecond _singletonSecond = null; /// <summary> /// 構造函數耗時耗資源 /// </summary> private SingletonSecond() { long lResult = 0; for (int i = 0; i < 10000000; i++) { lResult += i; } Thread.Sleep(1000); Console.WriteLine("{0}被構造一次", this.GetType().Name); } /// <summary> /// 靜態構造函數:由CLR保證,程式第一次使用這個類型前被調用,且只調用一次。 /// </summary> static SingletonSecond() { _singletonSecond = new SingletonSecond(); Console.WriteLine("SingletonSecond 被啟動"); } /// <summary> /// 餓漢式 只要使用類就會被構造 /// </summary> /// <returns> /// </returns> public static SingletonSecond CreateInstance() { return _singletonSecond; } } }
1.3、餓漢式寫法(靜態欄位)
using System; using System.Threading; namespace SingletonPattern { /// <summary> /// 餓漢式 /// </summary> public class SingletonThird { /// <summary> /// 靜態欄位:在第一次使用這個類之前,由CLR保證,初始化且只初始化一次。 /// 這個比構造函數還早 /// </summary> private static SingletonThird _singletonThird = new SingletonThird(); //列印個日誌 /// <summary> /// 構造函數耗時耗資源 /// </summary> private SingletonThird() { long lResult = 0; for (int i = 0; i < 10000000; i++) { lResult += i; } Thread.Sleep(1000); Console.WriteLine("{0}被構造一次", this.GetType().Name); } /// <summary> /// 餓漢式 只要使用類就會被構造 /// </summary> /// <returns> /// </returns> public static SingletonThird CreateInstance() { return _singletonThird; } public void Show() { Console.WriteLine("這裡是{0}.Show", this.GetType().Name); } } }
2、原型模式(Prototype Pattern)
原型模式:
換個方式創建對象,不走構造函數,而是記憶體拷貝。
單例的基礎上升級了一下,把對象從記憶體層面複製了一下,然後返回。
是個新對象,但是又不是new出來的。
using System; using System.Threading; namespace PrototypePattern { /// <summary> /// 原型模式:單例的基礎上升級了一下,把對象從記憶體層面複製了一下,然後返回。 /// 是個新對象,但是又不是new出來的。 /// </summary> public class Prototype { /// <summary> /// 構造函數耗時耗資源 /// </summary> private Prototype() { long lResult = 0; for (int i = 0; i < 10000000; i++) { lResult += i; } Thread.Sleep(2000); Console.WriteLine("{0}被構造一次", this.GetType().Name); } /// <summary> /// 全局唯一靜態 重用這個變數 /// </summary> private static volatile Prototype _prototype = new Prototype(); /// <summary> /// 公開的靜態方法提供對象實例 /// </summary> /// <returns> /// </returns> public static Prototype CreateInstance() { Prototype prototype = (Prototype)_prototype.MemberwiseClone(); //從記憶體層面複製 return prototype; } } }
下麵為了演示鼎鼎大名的三大工廠我們創建幾個介面和類:
/// <summary> /// 種族 /// </summary> public interface IRace { void ShowKing(); } /// <summary> /// 軍隊 /// </summary> public interface IArmy { void ShowArmy(); } /// <summary> /// 英雄 /// </summary> public interface IHero { void ShowHero(); } /// <summary> /// 資源 /// </summary> public interface IResource { void ShowResource(); } /// <summary> /// 幸運值 /// </summary> public interface ILuck { void ShowLuck(); }
using System; using FactoryPattern.War3.Interface; namespace FactoryPattern.War3.Service { /// <summary> /// 人族(War3種族之一) /// </summary> public class Human : IRace { public Human(int id, DateTime dateTime, string reamrk) { } public Human() { } public void ShowKing() { Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "Sky"); } } public class HumanArmy : IArmy { public void ShowArmy() { Console.WriteLine("The Army of {0} is {1}", this.GetType().Name, "footman,火槍,騎士,獅鷲"); } } public class HumanHero : IHero { public void ShowHero() { Console.WriteLine("The Army of {0} is {1}", this.GetType().Name, "大法師、山丘、聖騎士、血法師"); } } public class HumanResource : IResource { public void ShowResource() { Console.WriteLine("The Army of {0} is {1}", this.GetType().Name, "1000G1000W"); } } }
using System; using FactoryPattern.War3.Interface; namespace FactoryPattern.War3.Service { /// <summary> /// 不死族(War3種族之一) /// </summary> public class Undead : IRace { public void ShowKing() { Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "GoStop"); } } public class UndeadArmy : IArmy { public void ShowArmy() { Console.WriteLine("The Army of {0} is {1}", this.GetType().Name, "食屍鬼,蜘蛛,雕像,戰車,憎惡,冰霜巨龍"); } } public class UndeadHero : IHero { public void ShowHero() { Console.WriteLine("The Army of {0} is {1}", this.GetType().Name, "DK、Lich、小強、恐懼魔王"); } } public class UndeadResource : IResource { public void ShowResource() { Console.WriteLine("The Army of {0} is {1}", this.GetType().Name, "1000G1000W"); } } }
using System; using FactoryPattern.War3.Interface; namespace FactoryPattern.War3.Service { /// <summary> /// War3種族之一 /// </summary> public class ORC : IRace { public void ShowKing() { Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "Grubby"); } } public class ORCArmy : IArmy { public void ShowArmy() { Console.WriteLine("The Army of {0} is {1}", this.GetType().Name, "大G、風騎士、蝙蝠、戰車、牛頭人"); } } public class ORCHero : IHero { public void ShowHero() { Console.WriteLine("The Army of {0} is {1}", this.GetType().Name, "劍聖、小薩滿、先知、牛頭人酋長"); } } public class ORCResource : IResource { public void ShowResource() { Console.WriteLine("The Army of {0} is {1}", this.GetType().Name, "1000G1000W"); } } }
using System; using FactoryPattern.War3.Interface; namespace FactoryPattern.War3.Service { /// <summary> /// War3種族之一 /// </summary> public class NE : IRace { public void ShowKing() { Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "Moon"); } } }
3、簡單工廠(Simple Factory)
簡單工廠:不直接new,把對象創建轉移到工廠類。(簡單工廠不屬於23種設計模式)
核心代碼如下:
using System; using FactoryPattern.War3.Interface; using FactoryPattern.War3.Service; namespace SimpleFactory { /// <summary> /// 簡單工廠 /// </summary> public class ObjectFactory { /// <summary> /// 細節沒有消失 只是轉移 /// 轉移了矛盾,並沒有消除矛盾 /// 此處集中了矛盾 /// </summary> /// <param name="raceType"> /// </param> /// <returns> /// </returns> public static IRace CreateRace(RaceType raceType) { IRace iRace; switch (raceType) { case RaceType.Human: iRace = new Human(); break; case RaceType.Undead: iRace = new Undead(); break; case RaceType.ORC: iRace = new ORC(); break; case RaceType.NE: iRace = new NE(); break; //每增加一個分支就需要修改代碼 default: throw new Exception("wrong raceType"); } return iRace; } } /// <summary> /// 種族類型枚舉 /// </summary> public enum RaceType { Human, Undead, ORC, NE } }
using System; using FactoryPattern.War3.Interface; namespace SimpleFactory { /// <summary> /// 玩家 /// </summary> public class Player { public int Id { get; set; } public string Name { get; set; } /// <summary> /// 面向抽象 /// </summary> /// <param name="race"> /// 種族 /// </param> public void PlayWar3(IRace race) { Console.WriteLine("******************************"); Console.WriteLine("This is {0} Play War3.{1}", this.Name, race.GetType().Name); race.ShowKing(); } } }
using System; using FactoryPattern.War3.Interface; using FactoryPattern.War3.Service; namespace SimpleFactory { /// <summary> /// 簡單工廠:非常簡單的工廠 /// 工廠就是創建對象的地方 /// </summary> class Program { static void Main(string[] args) { try { Player player = new Player() { Id = 123, Name = "候鳥" }; { Human human = new Human();//1 到處都是細節 player.PlayWar3(human); } { IRace human = new Human();//2 左邊是抽象 右邊是細節 player.PlayWar3(human); } { IRace human = ObjectFactory.CreateRace(RaceType.Human); //3 沒有細節 細節被轉移 player.PlayWar3(human); } { IRace undead = ObjectFactory.CreateRace(RaceType.Undead); //4 沒有細節 細節被轉移 player.PlayWar3(undead); } } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.Read(); } } }
4、工廠方法模式(Factory Method Pattern)
工廠方法模式:
屏蔽對象的創建,留下了擴展空間。
把簡單工廠拆分成多個工廠,保證每個工廠的相對穩定。
多new一次工廠,難免,中間層,屏蔽業務類變化的影響,而且可以留下創建對象的擴展空間。
核心代碼如下:
using FactoryPattern.War3.Interface; namespace FactoryMethod.Factory { public interface IFactory { /// <summary> /// 創建種族 /// </summary> /// <returns> /// </returns> IRace CreateRace(); } }
using System; using FactoryPattern.War3.Interface; using FactoryPattern.War3.Service; namespace FactoryMethod.Factory { public class HumanFactory : IFactory { public virtual IRace CreateRace() { return new Human(); } } /// <summary> /// 後期可以對其擴展 /// </summary> public class HumanFactoryAdvanced : HumanFactory { public override IRace CreateRace() { Console.WriteLine("123"); return new Human(); } } }
using FactoryPattern.War3.Interface; using FactoryPattern.War3.Service; namespace FactoryMethod.Factory { public class UndeadFactory : IFactory { public IRace CreateRace() { return new Undead(); } } }
using FactoryPattern.War3.Interface; using FactoryPattern.War3.Service; namespace FactoryMethod.Factory { public class ORCFactory : IFactory { public IRace CreateRace() { return new ORC(); } } }
using FactoryPattern.War3.Interface; using FactoryPattern.War3.Service; namespace FactoryMethod.Factory { public class NEFactory : IFactory { public IRace CreateRace() { return new NE(); } } }
using System; using FactoryMethod.Factory; using FactoryPattern.War3.Interface; namespace FactoryMethod { /// <summary> /// 工廠方法:把簡單工廠拆分成多個工廠,保證每個工廠的相對穩定。 /// 但是要多new一次工廠? 難免,中間層,屏蔽業務類變化的影響,而且可以留下創建對象的擴展空間。 /// 開閉原則:對擴展開發,對修改封閉。 /// 工廠方法完美遵循了開閉原則 /// </summary> class Program { static void Main(string[] args) { try { { //human IFactory factory = new HumanFactory();//包一層 IRace race = factory.CreateRace(); //何苦 搞了這麼多工廠 還不是創建個對象 //以前依賴的是Human 現在換成了HumanFactory //1 工廠可以增加一些創建邏輯 屏蔽對象實例化的複雜度 //2 對象創建的過程中 可能擴展(尤其是ioc) } { //Undead IFactory factory = new UndeadFactory(); IRace race = factory.CreateRace(); } } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.Read(); } } }
可以看出工廠方法模式,就是把簡單工廠拆分成多個工廠,保證每個工廠的相對穩定。多new一次工廠,難免,中間層,屏蔽業務類變化的影響,而且可以留下創建對象的擴展空間。
另外工廠方法完美遵循了開閉原則,例如:Demo中原先我們有4個種族,分別為Human、Undead、ORC和NE,此時如果業務發生變化需要增加一個Five種族,
這時候我們只需要添加一個Five工廠類就好了,不會影響原來的代碼。
using System; using FactoryPattern.War3.Interface; namespace FactoryPattern.War3.ServiceExtend { /// <summary> /// War3種族之一 /// </summary> public class Five : IRace { public Five() : this(1, "old", 1) //當前類的構造函數 { } public Five(int id, string name, int version) { } public void ShowKing() { Console.WriteLine("The King of {0} is {1}", this.GetType().Name, "Moon"); } } }
using FactoryPattern.War3.Interface; using FactoryPattern.War3.ServiceExtend; namespace FactoryMethod.Factory { /// <summary> /// 比如構造很複雜。。比如依賴其他對象 /// 屏蔽變化 /// </summary> public class FiveFactory : IFactory { public virtual IRace CreateRace() { //return new Five(); return new Five(2, "New", 2); } } }
5、抽象工廠模式(Abstract Factory Pattern)