[.NET] IoC (Inversion of Control) 實作工廠模式

摘要:[ASP.NET] IoC (Inversion of Control) 實作工廠模式

前言


IoC 全名 Inversion of Control,中文稱 控制反轉

IoC 是設計模式的一種,主要用來降低偶合性並且能夠抽換工作類別,

因為當程式開發到一定的規模時,程式偶合性越高之後的維護就越困難,

並且會造成動了一個地方的程式卻要更改一堆連帶的程式碼問題。

 

範例


舉個例來說,一般類別間呼叫使用的撰寫方法如下:

我本身為一個MAN類別,小狗為一個Dog類別,

MAN類別中有: goEat方法(去吃東西)、goMove方法(去動動)、goSleep方法(去睡覺),

DOG類別中有: eat方法(吃)、move方法(動)、sleep方法(睡),

程式碼如下:


        static void Main(string[] args)
        {
            Console.WriteLine("主人:我來了!");
            Dog dog = new Dog();
            bool flag = true;
            while (flag)
            {
                string cmd = Console.ReadLine();
                if (cmd == "eat")
                    goEat(dog);
                else if (cmd == "move")
                    goMove(dog);
                else if (cmd == "sleep")
                    goSleep(dog);
                else
                {
                    flag = false;
                    Console.WriteLine("沒事,我走了!");
                }
            }

            Console.ReadLine(); 
        }

        static void goEat(Dog dog)
        {
            dog.eat();
        }

        static void goMove(Dog dog)
        {
            dog.move();
        }

        static void goSleep(Dog dog)
        {
            dog.sleep();
        }

執行結果如下:

 

看起來好像其實很正常沒甚麼問題,

但是,如果女朋友說: 寶貝~我又想養一隻貓耶,我要養~ >.^ 咪揪

這時該怎麼辦?

 

上面這種程式碼是不是就變成要去修改原始程式,要多加了貓的判斷跟動作?,如下:


        static void Main(string[] args)
        {
            Console.WriteLine("主人:我來了!");
            Console.Write("你要找(dog/cat)?: ");
            string animalType = Console.ReadLine();
            if (animalType == "cat")
            {
                Cat cat = new Cat();
                bool flag = true;
                while (flag)
                {
                    Console.Write("下指令(eat/move/sleep/bye): ");
                    string cmd = Console.ReadLine();
                    if (cmd == "eat")
                        goEat(cat);
                    else if (cmd == "move")
                        goMove(cat);
                    else if (cmd == "sleep")
                        goSleep(cat);
                    else
                    {
                        flag = false;
                        Console.WriteLine("沒事,我走了!");
                    }
                }
            }
            else if (animalType == "dog")
            {
                Dog dog = new Dog();
                bool flag = true;
                while (flag)
                {
                    Console.Write("下指令(eat/move/sleep/bye): ");
                    string cmd = Console.ReadLine();
                    if (cmd == "eat")
                        goEat(dog);
                    else if (cmd == "move")
                        goMove(dog);
                    else if (cmd == "sleep")
                        goSleep(dog);
                    else
                    {
                        flag = false;
                        Console.WriteLine("沒事,我走了!");
                    }
                }
            }
           

            Console.ReadLine(); 
        }

        static void goEat(Dog dog)
        {
            dog.eat();
        }

        static void goMove(Dog dog)
        {
            dog.move();
        }

        static void goSleep(Dog dog)
        {
            dog.sleep();
        }

        static void goEat(Cat cat)
        {
            cat.eat();
        }

        static void goMove(Cat cat)
        {
            cat.move();
        }

        static void goSleep(Cat cat)
        {
            cat.sleep();
        }

 

各位應該都發現了,

因為多了一隻寵物導致程式碼變得很冗長又複雜很難維護,而如果女朋友哪天心血來潮又要養什麼的話,可想而知慘兮兮。

 

接下來讓我們使用 IoC 來設計看看,

我重新設計類別,產生了一個 IAnimal 的介面,再多了一個Bird類別,並且Dog、Cat、Bird三個類別都實作 IAnimal 介面,如下:

 

接下來,我新增一個Factory 類別,使用 Factory Class 控管這些小動物類別,

並加入一個 CallAnimal 方法作為入口,用來實際產生該類別。

程式碼如下:


        public static IAnimal CallAnimal(string type)
        {
            switch (type)
            {
                case "dog":
                    return new Dog();
                case "cat":
                    return new Cat();
                case "bird":
                    return new Bird();
                default:
                    return null;
            }
        }

 

這邊使用swicth來選擇要分配哪一個物件回去,因為Dog、Cat、Bird三類別都有實作 IAnimal 介面,

所以回傳型別直接使用 IAnimal 介面,

接下來修改MAN類別,如下,


        static void Main(string[] args)
        {
            Console.WriteLine("主人:我來了!");
            Console.Write("你要找(dog/cat/bird)?: ");
            string animalType = Console.ReadLine();
            IAnimal animal = AnimalFactory.CallAnimal(animalType);
            if (animal != null)
            {
                bool flag = true;
                while (flag)
                {
                    Console.Write("下指令(eat/move/sleep/bye): ");
                    string cmd = Console.ReadLine();
                    if (cmd == "eat")
                        goEat(animal);
                    else if (cmd == "move")
                        goMove(animal);
                    else if (cmd == "sleep")
                        goSleep(animal);
                    else
                    {
                        flag = false;
                        Console.WriteLine("沒事,我走了!");
                    }
                }
            }
            Console.ReadLine(); 
        }

        static void goEat(IAnimal anim)
        {
            anim.eat();
        }

        static void goMove(IAnimal anim)
        {
            anim.move();
        }

        static void goSleep(IAnimal anim)
        {
            anim.sleep();
        }

 

這邊我使用 IAnimal 介面去接回傳回來的動物物件,因為 dog、cat、bird三類別都有實作 IAnimal 介面方法,

所以都能夠使用 eat()、move()、sleep()方法,而我們只需要依照要使用的方法去呼叫就可以了,

看完上例應該有發現之後不管再新增多少動物,在此設計模式下都可以簡單的抽換動物類別,

只要新增其對應的動物類別跟稍為修改 AnimalFactory 類別的 swicth 即可,拼拼裝裝就能夠完成,女朋友要養什麼都沒問題啦!!

 

2012/09/19 更新:

後來想了一下,因為這種方法最後還是會需要去動到 Factory 程式,所以我再稍為修改一下,

讓程式之後只需要讀取 app.config 組態檔設定即可,也就是只要新增一個Animal Class與修改組態檔即可,

方法如下:

1.加入 app.config 組態檔


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="AnimalObject1" value="Dog" />
    <add key="AnimalObject2" value="Cat" />
    <add key="AnimalObject3" value="Bird" />
  </appSettings>
</configuration>

我加入三個 AnimalObject 設定字串,各代表不同動物類別。

 

2.修改 AnimalFactory Class如下:


        public static IAnimal CallAnimal(string animalType)
        {
            List objNames = new List<string>();
            foreach (string key in System.Configuration.ConfigurationManager.AppSettings.Keys)
            {
                if (key.IndexOf("AnimalObject") > -1) {
                    objNames.Add(System.Configuration.ConfigurationManager.AppSettings[key].ToString());
                }
            }
            foreach (string name in objNames)
            {
                if (name == animalType)
                {
                    Type oType = Type.GetType(name);
                    IAnimal oAnimal = (IAnimal)Activator.CreateInstance(oType);
                    return oAnimal;
                }
            }
            return null;
        }

這裡的處理方式為,取得 appSetting 設定字串並且尋找是否有 AnimalObject 的字串,

找到之後產生該物件實體回傳,如此感覺上更方便了。

 

以上為使用心得備記,如有錯誤請多指教,謝謝。

 

範例程式碼


TIoC.rar

TIoC_2.rar

 

參考資料


http://baike.baidu.com/view/146665.htm

http://eoffice.im.fju.edu.tw/phpbb/viewtopic.php?p=29589

http://www.dotblogs.com.tw/hatelove/archive/2011/05/26/refactoring-using-interface-to-ioc.aspx

http://blog.csdn.net/code6421/article/details/1282139

 

 


以上文章敘述如有錯誤及觀念不正確,請不吝嗇指教
如有侵權內容也請您與我反應~謝謝您 :)