簡單工廠模式---一個簡單計算器的實現
1.面向物件程式設計
所有的程式設計初學者都會有這樣的問題,就是碰到問題就直覺地用計算機能夠理解的邏輯來描述和表達待解決的問題及具體的求解過程。這其實就是用計算機的方式去思考,是一種面向過程的開發方式,比如計算器這個程式,先要求輸入兩個數和運算子號,然後根據運算子號判斷選擇如何運算,得到結果,這本身沒有錯,但這樣的思維卻使得我們的程式只為滿足實現當前的需求,程式不容易維護、不容易擴充套件,更不容易複用,從而達不到高質量程式碼的要求。對應於下面的V1.0版本的程式碼
class Program { static void Main(string[] args) { try { Console.Write("請輸入數字A: "); string strNumberA = Console.ReadLine(); Console.Write("請選擇運算子號(+、-、*、/): "); string strOperate = Console.ReadLine(); Console.Write("請輸入數字B: "); string strNumberB = Console.ReadLine(); string strResult = ""; switch(strOperate) { case "+": strResult = Convert.ToString(Convert.ToDouble(strNumberA) + Convert.ToDouble(strNumberB)); break; case "-": strResult = Convert.ToString(Convert.ToDouble(strNumberA) - Convert.ToDouble(strNumberB)); break; case "*": strResult = Convert.ToString(Convert.ToDouble(strNumberA) * Convert.ToDouble(strNumberB)); break; case "/": if(strNumberB != "0") strResult = Convert.ToString(Convert.ToDouble(strNumberA) / Convert.ToDouble(strNumberB)); else strResult = "除數不能為0"; break; } Console.WriteLine("結果是: " + strResult); Console.ReadLine(); } catch(Exception ex) { Console.WriteLine("您的輸入有錯: " + ex.Message); } } }
面向物件的分析設計程式設計思想,通過封裝、繼承和多型把程式的耦合度降低,使用設計模式使得程式更加的靈活,容易修改,並且易於複用。
第一次迭代:讓計算業務邏輯和介面邏輯(這裡的介面是控制檯,也許會是windows MFC介面、Web頁面等等)分離開,讓它們之間的耦合度降低,只有分離開,才可以達到容易維護或者擴充套件。因此我們封裝一個Operation運算類專門來處理計算業務邏輯。下面是V2.0版本的程式碼:
//Operation運算類 public class Operation { public static double GetResult(double numberA, double numberB, string operate) { double result = 0; switch(operate) { case "+": result = numberA + numberB; break; case "-": result = numberA - numberB; break; case "*": result = numberA * numberB; break; case "/": result = numberA / numberB; break; } return result; } } //客戶端程式碼 class Program { static void Main(string[] args) { try { Console.Write("請輸入數字A: "); string strNumberA = Console.ReadLine(); Console.Write("請選擇運算子號(+、-、*、/): "); string strOperate = Console.ReadLine(); Console.Write("請輸入數字B: "); string strNumberB = Console.ReadLine(); string strResult = ""; strResult = Convert.ToString(Operation.GetResult(Convert.ToDouble(strNumberA), Convert.ToDouble(strNumberB), strOperate)); Console.WriteLine("結果是: " + strResult); Console.ReadLine(); } catch(Exception ex) { Console.WriteLine("您的輸入有錯: " + ex.Message); } } }
如果此時要寫一個其他介面應用程式的計算器,那麼這個運算類(Operation類)就可以複用了。
第二次迭代:現在如果我們希望在加減乘除這四種運算之外再增加一種開根(sqrt)運算,那麼我們只能修改Operation類的實現程式碼了,在switch語句中加入一個開根運算的分支並實現相應的運算邏輯。這樣做的話有一個風險:我們只需要增加一個開根運算,卻需要讓加減乘除的運算都得來參與編譯,如果一不小心將加法運算改成了減法運算,那麼就大大不妙了。本來是想增加一個功能,卻使得原有的執行良好的功能程式碼產生了變化,這個風險太大了。那麼我們應該把加減乘除等運算分離開來,修改其中的一個不影響另外的幾個,增加運算演算法也不影響其他的程式碼。我們還專門用一個“運算工廠”類來根據不同的運算子號來產生不同的運算演算法物件,比如如果提供的是一個“+”,那麼就產生一個OperationAdd類的物件來完成加法運算,這裡我們需要使用面向物件的繼承和多型特性。修改後的V3.0版本的程式碼如下:
//Operation運算類
//(這裡作為其他運算演算法的父類,向子類提供GetResult虛方法,具體演算法由子類實現)
public class Operation
{
private double _numberA = 0;
private double _numberB = 0;
public double NumberA
{
get { return _numberA; }
set { _numberA = value; }
}
public double NumberB
{
get { return _numberB; }
set { _numberB = value; }
}
public virtual double GetResult()
{
double result = 0;
return result;
}
}
//加減乘除類
class OperationAdd : Operation
{
public override double GetResult()
{
double result = 0;
result = NumberA + NumberB;
return result;
}
}
class OperationSub : Operation
{
public override double GetResult()
{
double result = 0;
result = NumberA - NumberB;
return result;
}
}
class OperationMul : Operation
{
public override double GetResult()
{
double result = 0;
result = NumberA * NumberB;
return result;
}
}
class OperationDiv : Operation
{
public override double GetResult()
{
double result = 0;
if(NumberB == 0)
throw new Exception("除數不能為0. ");
result = NumberA / NumberB;
return result;
}
}
//Operation工廠類
public class OperationFactory
{
public static Operation createOperate(string operate)
{//根據運算子的不同產生不同的運算演算法物件,這裡返回的是Operation父類物件的引用,但本質其實是指向子類物件的
//根據面向物件的多型特性,由於GetResult方法是一個虛方法,最終會呼叫的是子類物件中的GetResult方法
Operation oper = null;
switch(operate)
{
case "+":
oper = new OperationAdd();
break;
case "-":
oper = new OperationSub();
break;
case "*":
oper = new OperationMul();
break;
case "/":
oper = new OperationDiv();
break;
}
return oper;
}
}
//客戶端程式碼
class Program
{
static void Main(string[] args)
{
try
{
Console.Write("請輸入數字A: ");
string strNumberA = Console.ReadLine();
Console.Write("請選擇運算子號(+、-、*、/): ");
string strOperate = Console.ReadLine();
Console.Write("請輸入數字B: ");
string strNumberB = Console.ReadLine();
string strResult = "";
Operation oper;
oper = OperationFactory.createOperate(strOperate);
oper.NumberA = Convert.ToDouble(strNumberA);
oper.NumberB = Convert.ToDouble(strNumberB);
strResult = Convert.ToString(oper.GetResult());
Console.WriteLine("結果是: " + strResult);
Console.ReadLine();
}
catch(Exception ex)
{
Console.WriteLine("您的輸入有錯: " + ex.Message);
}
}
}
現在考慮下這種設計的可維護性和可擴充套件性如何:
1.假如現在要修改一個具體的演算法業務邏輯,比如要修改加法運算,那麼我們只需要修改OperationAdd類就可以了,與其他的運算演算法無關。
2.假如需要增加各種複雜運算,比如平方根、立方根、自然對數、正弦餘弦等,那麼我們只需要增加相應的Operation類的子類並且在OperationFactory工廠類的switch語句中增加分支即可,與其他的運算演算法無關。