簡單工廠模式,工廠方法模式,抽象工廠模式,spring的狂想

      菜鳥D在項目中碰見一個比較糾結的高耦合,因此就想辦法來解耦。狀況是這樣的:系統經過用戶選擇treeview控件的節點判斷調用不一樣的處理,這些處理中某些東西又是相似的。同事的建議是採用簡單工廠,耦合就耦合吧,反正treeview節點基本是不會變化的。(能偷懶就偷懶吧)html

      菜鳥D有些偏執,想找些方法來解耦。因而就學習了這個幾種方法,雖然不必定用的上,多學一點老是好的。spring

      首先說簡單工廠,例子是一個已經二到死的計算器。設計模式

      簡單工廠由三種角色組成:工廠類角色(creator),抽象產品類角色(product,圖中爲Iproduct接口),具體產品類角色(concreteproduct,圖中爲Product_A…)。ide

    代碼以下:學習

public class Calculate
    {
        public double DValue1 { get; set; }
        public double DValue2 { get; set; }

        public virtual double GetResult()
        {
            return 0;
        }
    }

    public class Add : Calculate
    {
        public override double GetResult()
        {
            return DValue1 + DValue2;
        }
    }

    public class Sub : Calculate
    {
        public override double GetResult()
        {
            return DValue1 - DValue2;
        }
    }

    public class Mul : Calculate
    {
        public override double GetResult()
        {
            return DValue1 * DValue2;
        }
    }

    public class Div : Calculate
    {
        public override double GetResult()
        {
            if (DValue2.Equals(0))
            {
                if (DValue1.Equals(0))
                {
                    return 1;
                }
                return 0;
            }
            return DValue1 / DValue2;
        }
    }
基本的代碼
            #region 客戶端
            Console.WriteLine("請輸入第一個數");
            string v1 = Console.ReadLine();
            Console.WriteLine("請輸入運算符");
            string oper = Console.ReadLine();
            Console.WriteLine("請輸入第二個數");
            string v2 = Console.ReadLine();
            Calculate c = GetCalculateByOper(oper);
            c.DValue1 = Convert.ToDouble(v1);
            c.DValue2 = Convert.ToDouble(v2);
            Console.WriteLine("結果:" + c.GetResult()); 
            #endregion

        #region 工廠方法
        private static Calculate GetCalculateByOper(string oper)
        {
            Calculate c = new Calculate();
            switch (oper)
            {
                case "+": c = new Add();
                    break;
                case "-": c = new Sub();
                    break;
                case "*": c = new Mul();
                    break;
                case "/": c = new Div();
                    break;
            }
            return c;
        } 
        #endregion    

      在這個例子中沒有使用抽象類,接口,而是用了虛方法(純粹是不想在接口裏定義屬性,本身喜歡用什麼就用什麼,畢竟這不是簡單工廠的核心)。簡單工廠的核心是充當工廠類角色的工廠方法(起碼在此例中是這樣),邏輯判斷中使具體產品類和工廠直接耦合了(好像類圖的關係已經決定了這種耦合,可是將具體產品和客戶端解耦)。若是要添加一個開方運算,首先添加開方的運算類,而後打開工廠方法進行修改。(呵呵,違反了開閉原則)ui

     接下來再說一下工廠方法模式,類圖以下:編碼

      先分析一下類圖,工廠模式有了四個角色:工廠接口,工廠實現,產品接口,產品實現(其實說角色不貼切,還不如說是種類)。工廠實現和產品實現有了依賴關係,經過調用工廠實現來獲取產品實現。也就是一種產品實現對應一種工廠實現。spa

     仍是那個二的要死的計算器的例子,代碼以下:.net

    public interface IFactory
    {
        ICalculatable GetCalculatable();
    }

    public interface ICalculatable
    {
        double GetReslut(double d1, double d2);
    }

    public class Add : ICalculatable
    {
        public double GetReslut(double d1, double d2)
        {
            return d1 + d2;
        }
    }

    public class AddFactory : IFactory
    {
        public ICalculatable GetCalculatable()
        {
            return new Add();
        }
    }

    public class Sub : ICalculatable
    {
        public double GetReslut(double d1, double d2)
        {
            return d1 - d2;
        }
    }

    public class SubFactory : IFactory
    {
        public ICalculatable GetCalculatable()
        {
            return new Sub();
        }
    }

    public class Mul : ICalculatable
    {
        public double GetReslut(double d1, double d2)
        {
            return d1 * d2;
        }
    }

    public class MulFactory : IFactory
    {
        public ICalculatable GetCalculatable()
        {
            return new Mul();
        }
    }

    public class Div : ICalculatable
    {
        public double GetReslut(double d1, double d2)
        {
            if (d2.Equals(0))
            {
                if (d1.Equals(0))
                {
                    return 1;
                }
                return 0;
            }
            return d1 / d2;
        }
    }

    public class DivFactory : IFactory
    {
        public ICalculatable GetCalculatable()
        {
            return new Div();
        }
    }
基本代碼
#region 邏輯判斷
    public class Factory
    {
        public static IFactory GetCalculatableByOper(string oper)
        {
            IFactory factory = null;
            switch (oper)
            {
                case "+": factory = new AddFactory();
                    break;
                case "-": factory = new SubFactory();
                    break;
                case "*": factory = new MulFactory();
                    break;
                case "/": factory = new DivFactory();
                    break;
            }
            return factory == null ? null : factory;
        }
    } 
#endregion

            #region 客戶端
            Console.WriteLine("請輸入第一個數");
            string v1 = Console.ReadLine();
            Console.WriteLine("請輸入運算符");
            string oper = Console.ReadLine();
            Console.WriteLine("請輸入第二個數");
            string v2 = Console.ReadLine();
            ICalculatable calculatable = Factory.GetCalculatableByOper(oper).GetCalculatable();
            Console.WriteLine("結果:" + calculatable.GetReslut(Convert.ToDouble(v1), Convert.ToDouble(v2))); 
            #endregion

      注意:此處的判斷邏輯已經不屬於工廠模式了(類圖中很明確),也能夠脫離客戶端。雖然說簡單工廠也能夠這樣將判斷邏輯脫離客戶端,可是簡單工廠的邏輯判斷是在工廠類中的,也就依然是簡單工廠模式的一部分(此處邏輯有些亂,不懂的就認真研究類圖)。一般在工廠模式的客戶端須要用到哪一種工廠再去建立哪一種工廠,而不是如例子中的使用一個判斷邏輯來選擇,也就是工廠模式建立工廠是經過「外部」的邏輯來肯定,而模式自己是不會作判斷的。(因此這個例子並非一個合適的例子)設計

      此時,計算器要再添加一個開方的方法,只需添加方法實現,添加工廠實現,不會對原有的代碼作修改,符合了開閉原則(你當我瞎啊,你不修改判斷邏輯嗎?說了多少次了,判斷邏輯部分不屬於工廠模式。不過比簡單工廠確實多寫很多東西)。

      工廠方法模式一般在如下兩種狀況中使用 :

      第一,須要使用某種產品,客戶端很清楚應該使用哪一個具體工廠,此種狀況不存在上述的判斷邏輯,只須要實例化具體工廠,而後生產具體產品。

      第二,須要使用某種產品,可是不想也不須要知道是哪一個工廠建立,此種狀況存在必定的判斷邏輯,可是客戶端不須要知道這個邏輯,生產方(工廠模式)是經過外部的邏輯來生產產品,這時由外部來實例化具體工廠,生產具體產品交付給客戶端。

      接着再來講說抽象工廠,在抽象工廠中最明顯的一個特色是產品不止一類了,因此前文提到的計算機的例子就不適合使用抽象工廠模式(竊覺得如此,有反對意見的歡迎提出)。

      在抽象工廠在提到一個「產品族」的概念,其實在抽象工廠中最爲直觀的體現就是產品A接口,產品B接口甚至是產品C、D接口。類圖以下,從類圖中不難發現,抽象工廠模式的具體工廠角色能夠生產多種產品(工廠方法模式的具體工廠只能生產一種產品,不信去看類圖)。就好像東方紅拖拉機廠在工廠方法模式下只能生產拖拉機,可是在抽象工廠模式下不只能生產拖拉機,還能生產卡車(若是加一個坦克的生產線,還能生產坦克)。

      此處就用比較熟悉的農場的例子,農場生產水果和蔬菜,水果分爲北方水果和熱帶水果,蔬菜也同樣,因此農場也分爲北方農場和熱帶農場。

      代碼以下:

    public interface IFruit
    {
        string Show();
    }

    public interface IVeggie
    {
        string Show();
    }

    public class NFruit : IFruit
    {
        private string name;
        public string Name
        {
            get { return "北方" + name; }
            set { name = value; }
        }

        public string Show()
        {
            return Name;
        }
    }

    public class TFruit : IFruit
    {
        string name;

        public string Name
        {
            get { return "熱帶" + name; }
            set { name = value; }
        }

        public string Show()
        {
            return Name;
        }
    }

    public class NVeggie : IVeggie
    {
        string name;

        public string Name
        {
            get { return "北方" + name; }
            set { name = value; }
        }


        public string Show()
        {
            return Name;
        }
    }

    public class TVeggie : IVeggie
    {
        string name;

        public string Name
        {
            get { return "熱帶" + name; }
            set { name = value; }
        }

        public string Show()
        {
            return Name;
        }
    }

    public interface Factory
    {
        IFruit CreateFruit(string nm);
        IVeggie CreateVeggie(string nm);
    }

    public class NFactory : Factory
    {
        public IFruit CreateFruit(string nm)
        {
            return new NFruit() { Name = nm };
        }

        public IVeggie CreateVeggie(string nm)
        {
            return new NVeggie() { Name = nm };
        }
    }

    public class TFactory : Factory
    {
        public IFruit CreateFruit(string nm)
        {
            return new TFruit() { Name = nm };
        }

        public IVeggie CreateVeggie(string nm)
        {
            return new TVeggie() { Name = nm };
        }
    }
基本代碼

      此例的邏輯判斷部分沒寫。從類圖上看,它是不屬於抽象工廠模式的,從簡化客戶端的角度,這個邏輯判斷是能夠從客戶端剝離的,很顯然和工廠方法模式的邏輯判斷屬於同一地位(有種兩頭受氣的感受)。

     而後再簡單說spring的依賴注入。在spring中須要獲取一個產品(實例)如何獲取?容器來提供。邏輯判斷呢?能夠放在客戶端,也能夠從客戶端剝離,愛放哪放哪。容器只依賴於配置,邏輯判斷不會影響容器。因此容器很好的將生產過程與客戶端隔離,這就不存在耦合了。

      接下來再說一下,一直不停的重複的判斷邏輯。在菜鳥D 的見解裏,上述幾個模式的耦合都是存在於判斷邏輯中的。在簡單工廠中,工廠和客戶端之間耦合較低,可是工廠和具體的產品類是直接耦合的。在工廠方法模式、抽象工廠模式、spring中,邏輯判斷只是一個輔助,邏輯判斷將生產過程和客戶端隔離,大大地下降了耦合程度。因此在衡量設計模式的耦合時,就須要衡量判斷邏輯在模式中做用和角色。有些耦合是很難避免的,爲了不這些耦合甚至可能會形成更多的耦合。至於開閉原則,編碼過程儘可能去注意,不然爲之後的開發帶來麻煩就不是咱們想要的了。

     一家之言,不值一哂,若有謬誤,歡迎指正。

     菜鳥D但願這篇文章對您有所幫助。

擴展參考:

大話設計模式P72:

      工廠方法實現時,客戶端須要決定實例化哪個工廠來實現運算類(具體產品),選擇判斷的問題仍是存在的,也就是說,工廠方法吧簡單工廠的內部邏輯判斷移到了客戶端代碼來進行.想要添加功能,原本是要改工廠類的,而如今是修改客戶端.

三種工廠模式區別總結

http://blog.csdn.net/lingfengtengfei/article/details/12374469

spring.net的依賴注入

http://www.cnblogs.com/GoodHelper/archive/2009/10/26/SpringNET_DI.html

工廠方法模式

http://blog.csdn.net/zhengzhb/article/details/7348707

三種工廠模式

http://www.cnblogs.com/poissonnotes/archive/2010/12/01/1893871.html

相關文章
相關標籤/搜索