1.4 面向對象編程
所有編程初學者碰到問題都會直覺地用計算機能夠理解的邏輯來描述和表達待解決的問題及具體的求解過程。這其實是用計算機的方式去思考,比如計算器這個程序:先要求輸入兩個數和運算符號,然后根據運算符號判斷選擇如何運算,得到結果,這本身沒有錯,但這樣的思維卻使得我們的程序只為滿足實現當前的需求。
簡單工廠模式與抽象工廠模式, 這樣的程序不容易維護,不容易擴展,更不容易復用。從而達不到高質量代碼的要求。
1.5 活字印刷,面向對象
曹操詩興大發,寫了一首詩:“喝酒唱歌,人生真爽……”,然后命印刷工匠刻版印刷以便流傳天下。
中庸第一章天命章? 樣張出來以后,曹操一看覺得過于俗氣,于是將“喝酒唱歌”改成了“對酒當歌”,于是就命工匠重新來過。工匠眼看連夜刻版之工徹底白費,心中叫苦不迭,但只得照辦。
樣張再次出來以后,曹操又覺得第二句寫的不好,于是將“人生真爽”改成了“人生幾何”,工匠得知后當即暈倒!
三國時期,由于活字印刷術還未發明,所以要改字的時候,就必須要整個刻版全部重新刻。如果有了活字印刷,則只需要改四個字即可,其余工作都未白做,豈不妙哉:
工廠到客戶叫什么模式? 第一,要改,只需更改要改之字,此為可維護;
第二,這些字并非用完這次就無用,完全可以在后來的印刷中重復使用,此為可復用;
第三,此詩若要加字,只需另刻字加入即可,這是可擴展;
抽象工廠模式與工廠方法模式, 第四,字的排列其實可能是豎排可能是橫排,此時只需將活字移動就可做到滿足滿足排列需求,這就是靈活性好。
而在活字印刷術出現之前,上面的四種特性都無法滿足,要修改,必須重刻,要加字,必須重刻,要重新排列,必須重刻,印完這本書后,此版已無任何可再利用價值。
1.6 面向對象的好處
大話設計模式知乎。 大鳥:“我以前也不懂,不過做了幾年軟件開發后,經歷了太多類似曹操這樣的客戶要改變需求、更改最初想法的事件,才逐漸明白當中的道理”。
其實客觀地說,客戶的要求也并不過分,不就是改幾個字嗎,但面對已完成的程序代碼,卻是需要幾乎重頭來過的尷尬,這實在是痛苦不堪。
說白了,原因就是因為我們原先所寫的程序,不容易維護,靈活性差,不容易擴展,更談不上復用,因此面對需求變化,加班加點,對程序動大手術的那種無奈也就成了非常正常的事了。
之后當我學習了面向對象的分析設計編程思想,開始考慮通過封裝、繼承、多態吧程序的耦合度降低,傳統印刷術的問題就在于所有的字都刻在同一版面上造成耦合度太高所致,開始用設計模式使得程序更加的靈活,容易修改,并且易于復用。這些都是面向對象帶來的好處。
1.7 復制 VS 復用
有人說初級程序員的工作就是 Ctrl + C 和 Ctrl + V,這其實是非常不好的編碼習慣,因為當你的代碼中重復的代碼多到一定程度,維護的時候,可能就是一場災難。—— 越大的系統,這種方式帶來的問題越嚴重,編程有一原則,就是用盡可能的辦法去避免重復。
1.8 業務的封裝
想想看,你寫的這段代碼,有哪些是和控制臺無關的,而只是和計算器有關的?
小菜:“你的意思是分一個類出來?哦,對的,讓計算和顯示分開”。
“準確地說,就是讓業務邏輯與界面邏輯分開,讓它們之間的耦合度下降。只有分離開,才可以達到容易維護或擴展”。
初級的面向對象代碼:
public class Operation {public static double GetResult(double numberA, double numberB, string operate){double result = 0d;switch (operate){case "+":result = numberA + numberB;breake;case "-":result = numberA - numberB;breake;case "*":result = numberA * numberB;breake;case "/":result = numberA / numberB;breake;}return result;} }
這種實現只是簡單的利用了面向對象的封裝特性,而沒有使用面向對象的繼承和多態兩個特性。
1.9 進一步的實現(松耦合)
operation 運算類:
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 GetResout(){double result = 0;return result;}}
加減乘除類:
/// <summary>/// 加法類,繼承運算類/// </summary>public class OperationAdd : Operation{public override double GetResout(){double result = 0;result = NumberA + NumberB;return result;}}/// <summary>/// 除法類,繼承運算類/// </summary>public class OperationDiv : Operation{public override double GetResout(){double result = 0;if (NumberB == 0)throw new Exception("除數不能為0。");result = NumberA / NumberB;return result;}}
這里首先寫了一個運算類,它有兩個 Number 屬性,主要用于計算器的前后數,然后有一個虛方法 GetResult(),用于得到結果。
然后我把加減乘除都寫成了運算類的子類,繼承它后重寫了?GetResult() 方法。—— 這樣(通過繼承)如果要修改任何一個算法,就不需要提供其他算法的代碼了。
1.10 簡單工廠模式
現在就是如何實例化對象的問題了,這里采用“簡單工廠模式”。
到底實例化誰,將來會不會增加實例化的對象,比如增加開根運算,這是很容易變化的地方,應該考慮用一個單獨的類來做這個創造實例的過程,這就是工廠。
簡單運算工廠類:
public class OperationFactory{public static Operation CreateOperate(string operate){Operation oper = null;switch (operate){case "+":oper = new OperationAdd();break;case "/":oper = new OperationDiv();break;}return oper;}}
這樣,只需要輸入運算符號,工廠就可以實例化出合適的對象。
客戶端代碼:
Operation oper;oper = OperationFactory.CreateOperate("+");oper.NumberA = 1;oper.NumberB = 2;double result = oper.GetResout();Console.WriteLine(result);Console.ReadLine();
編程是一門技術,更加是一門藝術,不能只滿足于寫完代碼運行結果正確就完事,時常考慮如何讓代碼更加簡練,更加容易維護,容易擴展和復用,只有這樣才可以真正得到提高。