天天看點

面向對象23種設計模式系列(一)- 建立型設計模式

本系列将和大家分享面向對象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元素則每個種族的代碼都需要修改。

繼續閱讀