工廠模式做爲很常見的設計模式,在平常工做中出鏡率很是高,程序員們必定要掌握它的用法喲,今天跟着老胡一塊兒來看看吧。git
如今先讓咱們來看一個例子吧,好比,要開發一個簡單的計算器,完成加減功能,經過命令行讀入形如1+1的公式,輸出2這個結果,讓咱們看看怎麼實現吧。程序員
這個版本里面,咱們不考慮使用模式,就按照最簡單的結構,怎麼方便怎麼來。設計模式
思路很是簡單,僅須要實現如下幾個方法框架
class Program { static int GetOperatorIndex(string input) { int operatorIndex = 0; for (; operatorIndex < input.Length; operatorIndex++) { if (!char.IsDigit(input[operatorIndex])) break; } return operatorIndex; } static int GetOp(string input, int startIndex, int size = -1) { string subStr; if (size == -1) { subStr = input.Substring(startIndex); } else { subStr = input.Substring(startIndex, size); } return int.Parse(subStr); } static int CalculateExpression(string input) { var operatorIndex = GetOperatorIndex(input); //獲得運算符索引 var op1 = GetOp(input, 0, operatorIndex); //獲得運算數1 var op2 = GetOp(input, operatorIndex + 1); //獲得運算數2 switch (input[operatorIndex]) { case '+': return op1 + op2; case '-': return op1 - op2; default: throw new Exception("not support"); } } static void Main(string[] args) { string input = Console.ReadLine(); while(!string.IsNullOrEmpty(input)) { var result = CalculateExpression(input); Console.WriteLine("={0}", result); input = Console.ReadLine(); } } }
代碼很是簡單,毋庸置疑,這個運算器是能夠正常工做的。這也多是咱們大部分人剛剛踏上工做崗位的時候可能會寫出的代碼。但它有着如下這些缺點:編碼
接下來,咱們引入咱們的主題,工廠方法模式。
命令行
工廠方法模式使用一個虛擬的工廠來完成產品構建(在這裏是運算符的構建,由於運算符是咱們這個程序中最具備變化的部分),經過把可變化的部分封裝在工廠類中以達到隔離變化的目的。咱們看看UML圖:
依葫蘆畫瓢,咱們設計思路以下:設計
關鍵代碼以下,其餘讀取操做數之類的代碼就不在贅述。code
interface IOperator { int Calculate(int op1, int p2); }
class AddOperator : IOperator { public int Calculate(int op1, int op2) { return op1 + op2; } } class SubtractOperator : IOperator { public int Calculate(int op1, int op2) { return op1 - op2; } }
interface IOperatorFactory { IOperator CreateOperator(char c); }
class OperatorFactory : IOperatorFactory { public IOperator CreateOperator(char c) { switch(c) { case '+': return new AddOperator(); case '-': return new SubtractOperator(); default: throw new Exception("Not support"); } } }
static IOperator GetOperator(string input, int operatorIndex) { IOperatorFactory f = new OperatorFactory(); return f.CreateOperator(input[operatorIndex]); } static int CalculateExpression(string input) { var operatorIndex = GetOperatorIndex(input); var op1 = GetOp(input, 0, operatorIndex); var op2 = GetOp(input, operatorIndex + 1); IOperator op = GetOperator(input, operatorIndex); return op.Calculate(op1, op2); }
這樣,咱們就用工廠方法從新寫了一次計算器,如今看看,好處有blog
自此,咱們已經在代碼裏面實現了工廠方法模式,但可能有朋友就會想,雖然如今擴展性加強了,可是新添加運算符仍是須要修改已有的工廠,這不是違反了開閉原則麼。。有沒有更好的辦法呢?固然是有的。
索引
想一想工廠方法那個版本,咱們爲何增長新的運算符就會不可避免的修改現有工廠?緣由就是咱們經過switch case來硬編碼「教導」工廠如何根據用戶輸入產生正確的運算符,那麼若是有一種方法可讓工廠自動學會發現新的運算符,那麼咱們的目的不就達到了?
嗯,我想聰明的朋友們已經知道了,用屬性嘛,在C#中,這種方法完成類的自描述,是最好不過了的。
咱們的設計思路以下:
代碼以下:
class OperatorDescriptionAttribute : Attribute { public char Symbol { get; } public OperatorDescriptionAttribute(char c) { Symbol = c; } }
[OperatorDescription('+')] class AddOperator : IOperator { public int Calculate(int op1, int op2) { return op1 + op2; } } [OperatorDescription('-')] class SubtractOperator : IOperator { public int Calculate(int op1, int op2) { return op1 - op2; } }
class OperatorFactory : IOperatorFactory { private Dictionary<char, IOperator> dict = new Dictionary<char, IOperator>(); public OperatorFactory() { Assembly assembly = Assembly.GetExecutingAssembly(); foreach (var type in assembly.GetTypes()) { if (typeof(IOperator).IsAssignableFrom (type) && !type.IsInterface) { var attribute = type.GetCustomAttribute<OperatorDescriptionAttribute>(); if(attribute != null) { dict[attribute.Symbol] = Activator.CreateInstance(type) as IOperator; } } } } public IOperator CreateOperator(char c) { if(!dict.ContainsKey(c)) { throw new Exception("Not support"); } return dict[c]; } }
通過這種改造,如今程序對擴展性支持已經很友好了,須要添加Multiple,只須要添加一個Multiple類就能夠,其餘代碼都不用修改,這樣就完美符合開閉原則了。
[OperatorDescription('*')] class MultipleOperator : IOperator { public int Calculate(int op1, int op2) { return op1 * op2; } }
這就是咱們怎麼一步步從最原始的代碼走過來,一點點重構讓代碼實現工廠方法模式,最終再完美支持開閉原則的過程,但願能幫助到你們。
其實關於最後那個經過標記屬性實現擴展,微軟有個MEF框架支持的很好,原理跟這個有點類似,有機會咱們再聊聊MEF。