誰說C語言不能面向對象(之一,前言)

    看到題目,我相信一定有很多人覺得,嗯,我確確實實是個逗比。各種權威資料上寫得非常清楚,C語言是面向過程的語言,C++,Java,OC,Python,JS等這些纔是面向對象的語言。這個逗比,竟然覺得C可以面向對象,看來他真的是個逗比。

    所以我在此先聲明一下我起這個標題,以及,寫這個系列文章的初衷。我並不是挑戰權威,說C是面向對象的語言。確實,C的確是面向過程的語言,其中並沒有包括任何面向對象的語法。而我通過這系列連載的文章,是想表達一個觀點,所謂的“面向對象”或是“面向過程”,其實指的是一種編程思路,而任何程序語言都只是個編程的工具而已。只不過,在當初設計C語言的時候,並沒有打算讓C支持面向對象的語法,因此,C語言可能更加適合去編寫一些面向過程的程序項目。但是,這並不代表,使用C語言就不能夠面向對象編程。

    所以,面向過程是C的天賦,與之相對比較經典的就是Java,它天生面向對象。那麼,我爲什麼非要讓C語言去做一些它不擅長的事情呢?原因很簡單,因爲我在工作和生活中,遇到了非常多雖然使用了面向對象的程序設計語言,但是,卻根本沒有好好利用面向對象的特性,寫出來的代碼,仍然是面向過程的,僅僅是把面向對象的語言作爲一種語法擴充,而並沒有做到面向對象。因此,我寫這系列的文章,用一個大多數人認爲它只能面向過程的語言——C語言來做一個反差對比,向大家詮釋,就連C這樣面向過程的語言,都能夠做到很優雅的面向對象,並且,使用了面向對象以後,優勢又非常明顯。那麼,當我們再使用本身就支持面向對象的語言的時候,如果再不能夠充分利用面向對象的特性,那真的是所謂暴殄天物了。

    在我個人看來,程序代碼,不僅僅是一個工具、一種工程方式、一種與計算機交流的方法而已。代碼更是一種藝術品,是和同行之間交流、表達設計與實現的美的體現。好的代碼就如同一篇詩情畫意的散文,能讓閱讀的人感受到作者在書寫時的環境和心情,能夠讓人感受到清晰的思路表達。而耦合性高、可讀性差的代碼,就好似沒有經過排版的草稿紙,雖然結果是正確的,但是過程、思路是一片混亂的,沒有美感。如果你覺得把代碼當做藝術品這個境界太高不容易接受的話,那我不妨換一個角度來解釋。如果給你一篇文章,讓你從這篇文章中找出一些關鍵的、有用的信息,與此同時再看看文章中有沒有出現什麼錯誤,幫助作者來改正。這種時候,如果這篇文章字跡清晰、表達清楚、情節優美,那我想,你一定願意去細細品味,仔細閱讀,也很容易從中找到需要的關鍵點,如果其中有什麼錯誤,也會變得比較明顯,容易改正。可是如果你拿到的是一篇字跡潦草,邏輯混亂,前言不搭後語的文章,那我想,你很難從中找到關鍵信息,而找到錯誤也會變得更難。

    對於我們的代碼來說,也是同樣的,我們很少會出現一個工程項目僅僅運行一次就再也不需要運行的情況。代碼總是需要維護的,也許是添加新的特性,也許是去消除bug打補丁,而這些維護工作,有可能是代碼的創造者來完成的,也可能是其他人來完成的。但不管是誰,要想維護代碼,首先,他就需要讀懂代碼,然後,根據程序的現象去定位問題,找到管理這個部分的相關代碼,分析後進行修改,然後還有測試驗證。如果,代碼維護人員拿到的是亂成一鍋粥的代碼,那這個維護成本可想而知。連讀懂代碼可能夠不容易做到,那更別說修改了。

    因此,編寫清晰、整潔的代碼,不僅是個人的品德修養,更是項目維護的需要,於人於己都至關重要。

    那麼,整潔的代碼與面向對象又有什麼關係呢?在我的理解,所謂整潔代碼,講究一個高內聚、低耦合。通俗來講就是,模塊的可拆分性,在模塊內部的流程都是與這個模塊強相關的,而單獨的模塊拿出來是可以獨立運行的,模塊間可以重新拼接組合。舉個簡單的例子來說,假如我要執行“買漢堡”這個操作,下面有兩種寫法,我們可以對比一下:

寫法1:
1.從椅子上站起來
2.邁左腳
3.邁右腳
4.如果到未到達門口則返回2
5.彎腰
6.拿起左鞋子穿在左腳上
7.拿起右鞋子穿在右腳上
8.直起身
9.開門
10.出門
11.關門
12.邁左腳
13.邁右腳
14.如果未到達轉交則返回12
15.轉向正確的方向
16.如果未到達漢堡店則返回12
17.問服務員有沒有漢堡,如果沒有,程序結束
18.付款
19.等待
20.如果漢堡沒有做好則返回19
21.拿到漢堡
22.程序結束
寫法2:
1.出門
2.步行至漢堡店
3.購買漢堡

出門:
1.起身
2.走向門口
3.換鞋
4.出門

走路:
1.邁左腳
2.邁右腳
3.如果未到達目的地則返回1

換鞋:
1.拿起左鞋子穿在左腳上
2.拿起右鞋子穿在右腳上

步行至某地:
1.走路
2.如果未遇到轉彎則返回1
3.如果未到達目的地則返回1

購買漢堡:
1.詢問服務員是否有漢堡,如果沒有則程序結束
2.付款
3.等待漢堡做好
4.拿到漢堡

等待某商品:
1.等待
2.如果該商品未準備就緒則返回1
3.拿到商品

    這兩種寫法中,很顯然,第二種寫法就更爲整潔,首先,我們在“購買漢堡”這個過程中,看到的步驟是很清晰的,我只要先出門,走到漢堡店,然後買漢堡這樣就可以了,而具體怎麼出門,怎麼走,怎麼買,這些細枝末節的事情應該在更細化的分支中去明確,而不像寫法1那樣,所有的細節全部都混在一起,而對於關鍵的步驟以及整體的劃分就很難讀得出來。

    另一方面,就是模塊的可重用性,如果按照寫法1這樣,我想再寫一個買薯條的,或者買書包的程序,那買漢堡的程序就和它完全沒有關係,所有的步驟都得重新去設計和編寫。但是,如果是寫法2的這樣,這些模塊就可以拿出來重新組合,像是出門、換鞋、走路這些過程是完全不需要變化的,而步行、購買這些過程,也僅僅是目的地和商品變了而已,我只需要修改參數即可,程序過程完全不需要改變。因此,這樣做,不僅提升了代碼可讀性,還提升了代碼重用性。

    當然,我相信,一定會有反對的聲音說,這樣做降低了性能。那麼我希望大家思考一個問題,如果說,因爲增加了調用過程而產生的額外開銷,這件事情比代碼可讀性和重構性還重要,爲什麼又會誕生這麼多新的程序語言?那我們直接用匯編寫程序不就好了嗎?隨着計算機的不斷髮展,軟硬件的不斷進步,這種函數出入棧的開銷,已經可以忽略不計了。相比而言,優化算法纔是能夠真正增加效率降低運行時間的方式。軟件越來越複雜,如果我們還是用着彙編在寫程序,我相信不會有現在所謂的AR和AI這些技術的誕生,程序員就會把幾乎所有的時間用在怎麼給硬件打交道上,而沒有時間再去用軟件創造新的東西。另外一方面就是我之前說過的維護成本。如果代碼的維護性很差,那麼,我們在這個基礎上進行的修改就會有跟高的概率不能解決問題,甚至引入新的問題,而程序本身又是很脆弱的,很容易就會造成程序崩潰無法運行。與這種風險相比,那些所謂的“額外開銷”,我想,應該就不再算什麼了。

    回到之前的話題,既然整潔代碼這麼重要,那麼,我們如何編寫整潔的代碼呢?業界有很多方法,關於如何編寫程序可以更容易做到高內聚、低耦合,增強代碼的可讀性和可維護性。這些我們稱爲“設計模式”,而這些設計模式最基礎的思路,就是面向對象(Object Oriented,以後簡稱OO),這就是整潔代碼與OO之間的關係。

    因此,之後我會持續連載此係列文章,以C語言爲例,來帶領大家體驗OO之美。

【本系列文章歸逗比老師全權擁有,允許轉載,但請務必在開頭標註轉載字樣以及轉載源連接。】

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章