本系列将和大家分享面向對象23種設計模式中常用的幾種設計模式,本章主要簡單介紹下建立型設計模式。
本章是面向對象23種設計模式系列開篇,首先我們來看下什麼是設計模式?
面向對象23種設計模式:
1、面向對象語言開發過程中,遇到的種種場景和問題,提出了解決方案和思路,沉澱下來就變成了設計模式。
2、解決具體問題的具體招數---套路---站在前輩的肩膀上。
3、沒有什麼設計模式是完美無缺的,一種設計模式就是解決一類問題,通常設計模式在解決一類問題的同時,還會帶來别的問題,我們設計者要做的事兒,就是要揚長避短,充分發揮長處!
設計模式可以大概分為三大類:
1、建立型設計模式:關注對象的建立。
2、結構型設計模式:關注類與類之間的關系。
3、行為型設計模式:關注對象和行為的分離。
我們要做的就是學習核心套路,這裡就不做過多的描述,如果有機會會通過具體例子再和大家分享。下面我們正式進入本章主題。
建立型設計模式:關注對象的建立。(5個)
1、單例模式(Singleton Pattern)
單例模式:
就是限制了對象的建立,重用了對象。保證程序中,某個類隻有一個執行個體。
即使是單例,變量也不是線程安全的,單例不是為了保證線程安全。
單例的好處就是單例,就是全局唯一的一個執行個體。
應對一些特殊情況,比如資料庫連接配接池(内置了資源) ,全局唯一号碼生成器。
單例可以避免重複建立,但是也會常駐記憶體,除非是真的有必要,否則就不要使用單例。
1.1、單例模式經典寫法(懶漢式)
using System;using System.Threading;namespace SingletonPattern
{ ///
/// 懶漢式單例模式(經典寫法) /// 單例類:一個構造對象很耗時耗資源類型。 ///
public class Singleton
{ ///
/// 構造函數耗時耗資源 ///
private Singleton()
{ long lResult = 0; for (int i = 0; i < 10000000; i++)
{
lResult += i;
}
Thread.Sleep(2000);
Console.WriteLine("{0}被構造一次", this.GetType().Name);
} ///
/// 全局唯一靜态 重用這個變量 /// volatile 促進線程安全 讓線程按順序操作 ///
private static volatile Singleton _singleton = null; ///
/// 引用類型對象 ///
private static readonly object lockSingleton = new object(); ///
/// 公開的靜态方法提供對象執行個體 ///
///
///
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; ///
/// 既然是單例,大家用的是同一個對象,用的是同一個方法,那還會并發嗎 還有線程安全問題嗎? /// 即使是單例,變量也不是線程安全的,單例不是為了保證線程安全。 ///
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
{ ///
/// 為什麼要有單例設計模式? /// 構造對象耗時耗資源,很多地方都需要去new, 這個方法 其他方法 其他類 ///
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));
}
{
Listtasks = new List(); 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
{ ///
/// 餓漢式 ///
public class SingletonSecond
{ private static SingletonSecond _singletonSecond = null; ///
/// 構造函數耗時耗資源 ///
private SingletonSecond()
{ long lResult = 0; for (int i = 0; i < 10000000; i++)
{
lResult += i;
}
Thread.Sleep(1000);
Console.WriteLine("{0}被構造一次", this.GetType().Name);
} ///
/// 靜态構造函數:由CLR保證,程式第一次使用這個類型前被調用,且隻調用一次。 ///
static SingletonSecond()
{
_singletonSecond = new SingletonSecond();
Console.WriteLine("SingletonSecond 被啟動");
} ///
/// 餓漢式 隻要使用類就會被構造 ///
///
///
public static SingletonSecond CreateInstance()
{ return _singletonSecond;
}
}
}
1.3、餓漢式寫法(靜态字段)
using System;using System.Threading;namespace SingletonPattern
{ ///
/// 餓漢式 ///
public class SingletonThird
{ ///
/// 靜态字段:在第一次使用這個類之前,由CLR保證,初始化且隻初始化一次。 /// 這個比構造函數還早 ///
private static SingletonThird _singletonThird = new SingletonThird(); //列印個日志
///
/// 構造函數耗時耗資源 ///
private SingletonThird()
{ long lResult = 0; for (int i = 0; i < 10000000; i++)
{
lResult += i;
}
Thread.Sleep(1000);
Console.WriteLine("{0}被構造一次", this.GetType().Name);
} ///
/// 餓漢式 隻要使用類就會被構造 ///
///
///
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
{ ///
/// 原型模式:單例的基礎上更新了一下,把對象從記憶體層面複制了一下,然後傳回。 /// 是個新對象,但是又不是new出來的。 ///
public class Prototype
{ ///
/// 構造函數耗時耗資源 ///
private Prototype()
{ long lResult = 0; for (int i = 0; i < 10000000; i++)
{
lResult += i;
}
Thread.Sleep(2000);
Console.WriteLine("{0}被構造一次", this.GetType().Name);
} ///
/// 全局唯一靜态 重用這個變量 ///
private static volatile Prototype _prototype = new Prototype(); ///
/// 公開的靜态方法提供對象執行個體 ///
///
///
public static Prototype CreateInstance()
{
Prototype prototype = (Prototype)_prototype.MemberwiseClone(); //從記憶體層面複制
return prototype;
}
}
}
下面為了示範鼎鼎大名的三大工廠我們建立幾個接口和類:
/// /// 種族/// public interface IRace
{ void ShowKing();
}/// /// 軍隊/// public interface IArmy
{ void ShowArmy();
}/// /// 英雄/// public interface IHero
{ void ShowHero();
}/// /// 資源/// public interface IResource
{ void ShowResource();
}/// /// 幸運值/// public interface ILuck
{ void ShowLuck();
}
using System;using FactoryPattern.War3.Interface;namespace FactoryPattern.War3.Service
{ ///
/// 人族(War3種族之一) ///
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
{ ///
/// 不死族(War3種族之一) ///
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
{ ///
/// War3種族之一 ///
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
{ ///
/// War3種族之一 ///
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
{ ///
/// 簡單工廠 ///
public class ObjectFactory
{ ///
/// 細節沒有消失 隻是轉移 /// 轉移了沖突,并沒有消除沖突 /// 此處集中了沖突 ///
///
///
///
///
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;
}
} ///
/// 種族類型枚舉 ///
public enum RaceType
{
Human,
Undead,
ORC,
NE
}
}
using System;using FactoryPattern.War3.Interface;namespace SimpleFactory
{ ///
/// 玩家 ///
public class Player
{ public int Id { get; set; } public string Name { get; set; } ///
/// 面向抽象 ///
///
/// 種族 ///
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
{ ///
/// 簡單工廠:非常簡單的工廠 /// 工廠就是建立對象的地方 ///
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
{ ///
/// 建立種族 ///
///
/// 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();
}
} ///
/// 後期可以對其擴充 ///
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
{ ///
/// 工廠方法:把簡單工廠拆分成多個工廠,保證每個工廠的相對穩定。 /// 但是要多new一次工廠? 難免,中間層,屏蔽業務類變化的影響,而且可以留下建立對象的擴充空間。 /// 開閉原則:對擴充開發,對修改封閉。 /// 工廠方法完美遵循了開閉原則 ///
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
{ ///
/// War3種族之一 ///
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
{ ///
/// 比如構造很複雜。。比如依賴其他對象 /// 屏蔽變化 ///
public class FiveFactory : IFactory
{ public virtual IRace CreateRace()
{ //return new Five();
return new Five(2, "New", 2);
}
}
}
5、抽象工廠模式(Abstract Factory Pattern)
抽象工廠模式:
屏蔽對象的建立,限制強制保障産品簇。
建立一組密不可分的對象。
建立産品簇:建立一組密不可分的對象。
工廠+限制
傾斜的可擴充性設計,擴充種族很友善,增加産品很麻煩。
核心代碼如下:
using FactoryPattern.War3.Interface;namespace AbstractFactory.Factory
{ ///
/// 一個工廠負責一些産品的建立 /// 産品簇 /// 單一職責就是建立完整的産品簇 ///
/// 繼承抽象類後,必須顯式的override父類的抽象方法。 ///
public abstract class FactoryAbstract
{ public abstract IRace CreateRace(); public abstract IArmy CreateArmy(); public abstract IHero CreateHero(); public abstract IResource CreateResource(); //傾斜的可擴充性設計:擴充種族很友善,增加産品(元素)很麻煩。 //public abstract ILuck CreateLuck(); //增加産品(元素)則每個種族的代碼都要修改 }
}
using FactoryPattern.War3.Interface;using FactoryPattern.War3.Service;namespace AbstractFactory.Factory
{ ///
/// 一個工廠負責一些産品的建立 ///
public class HumanFactory : FactoryAbstract
{ public override IRace CreateRace()
{ return new Human();
} public override IArmy CreateArmy()
{ return new HumanArmy();
} public override IHero CreateHero()
{ return new HumanHero();
} public override IResource CreateResource()
{ return new HumanResource();
}
}
}
using FactoryPattern.War3.Interface;using FactoryPattern.War3.Service;namespace AbstractFactory.Factory
{ ///
/// 一個工廠負責一些産品的建立 ///
public class UndeadFactory : FactoryAbstract
{ public override IRace CreateRace()
{ return new Undead();
} public override IArmy CreateArmy()
{ return new UndeadArmy();
} public override IHero CreateHero()
{ return new UndeadHero();
} public override IResource CreateResource()
{ return new UndeadResource();
}
}
}
using System;using AbstractFactory.Factory;using FactoryPattern.War3.Interface;using FactoryPattern.War3.Service;namespace AbstractFactory
{ ///
/// 抽象工廠:建立一組密不可分的對象。 /// 建立産品簇:多個對象是個整體,不可分割。 ///
/// 工廠+限制 ///
/// 傾斜的可擴充性設計:擴充種族很友善,增加産品(元素)很麻煩。 ///
class Program
{ static void Main(string[] args)
{ try
{
Console.WriteLine("想要玩一款遊戲,必須4大元素備齊"); //System.Data.SqlClient.SqlClientFactory //使用的就是抽象工廠模式 {
IRace race = new Undead();
IArmy army = new UndeadArmy();
IHero hero = new UndeadHero();
IResource resource = new UndeadResource(); //1 對象轉移,屏蔽細節,讓使用者更輕松 //2 對象簇的工廠 }
{
FactoryAbstract undeadFactory = new UndeadFactory();
IRace race = undeadFactory.CreateRace();// new Undead();
IArmy army = undeadFactory.CreateArmy();//new UndeadArmy();
IHero hero = undeadFactory.CreateHero();//new UndeadHero();
IResource resource = undeadFactory.CreateResource();//new UndeadResource(); }
{
FactoryAbstract humanFactory = new HumanFactory();
IRace race = humanFactory.CreateRace();
IArmy army = humanFactory.CreateArmy();
IHero hero = humanFactory.CreateHero();
IResource resource = humanFactory.CreateResource();
}
} catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.Read();
}
}
}
可以看出抽象工廠模式,屏蔽對象的建立,限制強制保障産品簇,建立一組密不可分的對象(例如:Demo中每個種族都有四個密不可分的元素,分别為Race、Army、Hero和Resource)。
另外抽象工廠模式是傾斜的可擴充性設計,擴充種族很友善,增加産品(元素)很麻煩,例如在Demo中如果産品簇增加一個Luck元素則每個種族的代碼都需要修改。