利用設計模式能夠使我們的代碼更靈活,更容易擴充,更容易維護。各種物件導向的程式設計語言都提供了基本相同的機制:比如類、繼承、派生、多態等等。但是又有各自的特色,C# 中的反射機制便是個很重要的工具,好好地利用就能夠在實際中發揮很大的作用。 我們來看一個例子: 我的程式中有需要一系列的對象,比如apple,orange…, 要想利用他們,我們就必須在程式中根據使用者需要,然後一個個調用 new 操作符來產生他們,這樣客戶程式就要知道相應的類的資訊,產生的程式碼顯然不夠靈活。我們能夠在代碼中不利用具體的類,而只是說明我們需要什麼,然後就能夠得到我們想要的對象嗎? 哦,我們都看設計模式,聽吧,很多人都在那裡鼓吹他們是如何如何的棒,我們看看怎麼樣利用他們來解決問題。目標明確了,那我們看看哪個能夠符合我們的需要。GoF的《設計模式》都看過吧,似懂非懂的看了一些,那我們看看能夠不能夠“湊”上去呢?J 嗯,我們的程式考慮的是對象怎麼建立的,建立型模式應該符合需要吧。然後我們瀏覽一下各模式的“意圖”部分。呵呵,第一個似乎就撞到彩了,抽象工廠,我們看看吧,“提供一個建立一系列相關或相互依賴對象的介面,而無需指定他們具體的類”,至少“無需指定他們具體的類”符合我們的需要。來看看他的結構吧: 我們的問題似乎用不到這麼複雜吧,只有orange,apple等等(應該就是product了),他們顯然是一類的,都是fruit,我們只要一個生產水果的工廠就能夠,左邊的繼承層次不要,只有一個FruitFactroy看看行不,先別管他正統不正統,實用就行J 下面的一些東西顯然是我們需要的:
| Public interface IFruit { } public class Orange:IFruit { public Orange() { Console.WriteLine("An orange is got!"); } } public class Apple:IFruit { public Apple() { Console.WriteLine("An apple is got!"); } } |
我們的FruitFactory應該是怎麼樣呢?上面的結構圖中他給的是CreateProductA,那好,我就MakeOrange,更有一個CreateProductB,俺MakeOrange還不行??
| public class FruitFactory { public Orange MakeOrange() { return new Orange(); } public Apple MakeApple() { return new Apple(); } } |
怎麼使用這個工廠呢?我們來寫下面的代碼:
| string FruitName = Console.ReadLine(); IFruit MyFruit = null; FruitFactory MyFruitFactory = new FruitFactory(); switch (FruitName) { case "Orange": MyFruit = MyFruitFactory.MakeOrange(); break; case "Apple": MyFruit = MyFruitFactory.MakeApple(); break; default: break; } |
編譯運行,然後在控制台輸入想要的東西,呵呵,成功了。沉浸在幸福中的您得意忘形了吧。 但是等等,他似乎還不完美,我假如想要pear,我既要在客戶代碼中的switch中加入判斷,又要在Factory 方法中加入MakePear方法,似乎不怎麼優雅。更好一點,在工廠中只提供一個方法,MakeFruit,然後傳遞進一個參數Name,代表我們想要的水果的名稱,這樣的話,似乎我們的客戶代碼中的那個switch就能夠不要了,相反,在FruitFactory中似乎需要一個,還等什麼呢?實現吧。
| FruitFactory: public class FruitFactory { public IFruit MakeFruit(string Name) { switch (Name) { case "Orange": return new Orange(); case "Apple": return new Apple(); default: return null; } } } |
客戶代碼:
| string FruitName = Console.ReadLine(); IFruit MyFruit; FruitFactory MyFruitFactory = new FruitFactory(); MyFruit = MyFruitFactory.MakeFruit(FruitName); |
這樣看起來好多了,至少我客戶代碼中不要再寫那麼一長串的判斷代碼了。 阿Q精神又在起作用,我們又沉浸在成功的喜悅中了。 嗯,代碼似乎能夠,應該沒有什麼改進了。但是似乎又有另外一個聲音在說: “除了一點……” “嗯? 等等,什嗎?” “FruitFactory也有switch啊,看起來也ugly啊!” “哼,肯定是看《重構》或是《TDD》了,怎麼需要那麼苛刻!反正閑著也是閑著,看看能夠改不?” 既然不要條件判斷,傳入的只有水果的名稱,假如Name = “Apple”,要產生一個Apple的對象,我需要new Apple(),假如我能夠這樣多好: new MakeItToClass(Name),把字串轉換成一個類。C#中雖然沒有上述文法,但是提供了相應的機制,那就是反射。其中一個重要的類就是System.Type類,他對於反射起著核心的作用。我們能夠使用 Type 對象的方法、欄位、屬性和嵌套類來尋找有關該類型的任何資訊。 另外一個重要的類就是System.Activator,他包含特定的方法,用以在本地或從遠程建立物件類型,或擷取對現有遠程對象的引用。 我們能夠先利用Type類擷取Name指定的類名的類的Type資訊,然後能夠根據這個資訊利用Activator建立對象。還等什麼呢?
| public class FruitFactory { public IFruit MakeFruit(string Name) { IFruit MyFruit = null; try { Type type = Type.GetType(Name,true); MyFruit = (IFruit)Activator.CreateInstance(type); } catch (TypeLoadException e) Console.WriteLine("I dont know this kind of fruit,exception caught - {0}" ,e.Message); return MyFruit; } } |