1. 程式人生 > >跟著專案學設計模式(五):簡單工廠+單例模式+靜態類+延遲載入

跟著專案學設計模式(五):簡單工廠+單例模式+靜態類+延遲載入

接上文,專案交付之後,我們的類庫開發人員發現自己穿越了,回到【設計模式——2、簡單工廠模式】這篇文章所在時間線的最末尾。

由於穿越所造成的蝴蝶效應,這個專案後期雖然確實需要擴充套件,但是隻是要增加五到六個產品類,並要求儘快交付,以便將關注點放到其他更有價值的專案中去,那趕快來擴充我們的簡單工廠吧。

    public class Factory
    {
        /// <summary>
        /// 靜態方法建立Product例項
        /// </summary>
        public static IProduct CreateProduct(string name)
        {
            switch(name)
            {
                case "USER":return new UserDal();break;
                case "GOODS":return new GoodsDal();break;
                case "HISTORY":return new HistoryDal();break;
                ...
            }
        }
    }

 程式碼很快寫完了,測試通過,但是下一步sonar程式碼質量檢測,在工廠類的CreateProduct方法裡卻發現兩個問題,

第一是return和break那裡有壞味道,也是,都已經return了,還怎麼break?(好吧,是我強行這麼寫只是為了推薦一下sonar),那就改成switch裡用變數賦值,最後再return那個變數吧。

第二是方法圈複雜度剛好超過10了,這個扎心了,沒法解決,重構吧!

基本思路:用字典代替switch分支

    //工廠類
    public class Factory
    {
        public static readonly Dictionary<string, IProduct> dictionary = new Dictionary<string, IProduct>()
        {
            { "USER", new UserDal() },
            { "GOODS", new GoodsDal() },
            { "HISTORY", new HistoryDal() },
            ...
        };
        /// <summary>
        /// 靜態方法建立Product例項
        /// </summary>
        public static IProduct CreateProduct(string name)
        {
            return dictionary[name];
        }
    }

現在圈複雜度的問題徹底解決了,我們通過靜態常量dictionary和靜態方法CreateProduct實現的,唯一的缺點是在第一次呼叫Factory的時候,會把所有物件建立一次,要知道這可是單層架構,所有的初始化工作都在裡面了,效能何在?加入Lazy<T>延遲載入後能很好地解決這個問題!

    //工廠類
    public class Factory
    {
        public static readonly Dictionary<string, Lazy<IProduct>> dictionary = new Dictionary<string, Lazy<IProduct>>()
        {
            { "USER", new Lazy<IProduct>(() => new UserDal(), true) },
            { "GOODS", new Lazy<IProduct>(() => new GoodsDal(), true) },
            { "HISTORY", new Lazy<IProduct>(() => new HistoryDal(), true) },
            ...
        };
        /// <summary>
        /// 靜態方法建立Product例項
        /// </summary>
        public static IProduct CreateProduct(string name)
        {
            return dictionary[name].Value;
        }
    }

現在產品物件只會在第一次呼叫的時候建立,而不是呼叫Factory的時候全部建立。

繼續來看,專案組覺得這個方案很有價值,要求以後用到簡單工廠的專案都這樣幹。做法就是所有的簡單工廠類必須去實現ISampleFactory這個介面。

    public interface ISampleFactory
    {
        Dictionary<string, Lazy<IProduct>> Products { get; set; }
        IProduct CreateProduct(string name);
    }

前面通過靜態常量和靜態方法做的工作,隨著介面的出現,全部作廢了。因為靜態和介面是宿敵了,一個是面向過程的方法集合,一個是面向物件的高階面向介面。接口裡定義的屬性和方法只有物件才能呼叫,而靜態類壓根就沒有物件。

實現介面的類不能是靜態的,類所實現的屬性和方法也不能是靜態的,所以靜態用不了,而且Products也從常量變成了屬性。

靜態被否定了,也只能用單例模式了。我們可以把Dictionary<string, Lazy<IProduct>> Products這個欄位的初始化放到私有建構函式裡,而單例模式只有一個例項,不就意味著Products只會被初始化1次嘍。

    //工廠類
    public class Factory : ISampleFactory
    {
        private static Factory instance;//靜態例項

        private Factory()
        {
            Products = new Dictionary<string, Lazy<IProduct>>
            {
               { "USER", new Lazy<IProduct>(() => new UserDal(), true) },
               { "GOODS", new Lazy<IProduct>(() => new GoodsDal(), true) },
               { "HISTORY", new Lazy<IProduct>(() => new HistoryDal(), true) },
               ...
            };
        }

        public static Factory GetInstance()//方法,方法中去例項化類.
        {
            if (instance == null)
            {
                instance = new Factory();
            }
            return instance;
        }

        public Dictionary<string, Lazy<IProduct>> Products { get; set; }

        IProduct ISampleFactory.CreateProduct(string name)
        {
            return Products[name].Value;
        }
    }

好了,運用單例模式完美的解決了介面與靜態的矛盾,單例模式真的是非常給力的設計模式。

待續。。。