編程是一門藝術

        IT行業是一個又特殊又普通的行業。在這個多元化、信息化的時代,人們對電腦、互聯網、智能家電等等名詞越來越熟悉,越來越依賴。而這些名詞背後有着一個神祕的行業——IT業。這個行業發展迅速,從早期的電腦、軟件、互聯網到現在的信息化、大數據、雲計算、人工智能等等。隨着這些熱門詞彙漸漸被人們所接受。行業內部的專業人士也越來越關注這方面的技術內容,但是對於編程的藝術卻被逐漸的淡化。人們不再討論編程的過程與實現,而是討論更模塊化的功能集成,把使用各大公司研發的框架、軟件產品當做學習對象,而把編程簡單的看作是“寫代碼”而已。

        在這個飛速發展的時代爲了能夠快速的研發軟件,能夠快速按時完成程序編寫工作,只好使用那些所謂的“流行編程語言”,比如C++、Java、Python、C#、JavaScript等等。它們高效、方便移植、可擴展、可集成。大家通常更在乎代碼所實現的功能,而忽略了編程代碼的過程。往往編寫幾行代碼,調用現成的類庫或函數庫就可以實現強大的功能。由於人們通常沉迷於結果而忽略了過程,因此越來越多的人不再注重代碼的質量,認爲只要能夠實現目標功能的代碼就是好代碼。而編程本身也被人逐漸看成是“寫代碼”而已。

 

        實際上,編程是一門高深的藝術,並非是“寫代碼”那樣的簡單。計算機的專業課非常多,而編程語言的學習卻很少,通常只有《C語言》和《C++語言》。然而除了編程語言的學習之外還要學習很多基礎課程,例如:《計算機組成原理》、《計算機體系結構》、《數據結構》、《算法導論》等等。另外,基礎數學方面的知識也是必學的:《高等數學》、《線性代數》、《概率統計》、《離散數學》等等。

        編程只是一個表象,在編程過程的背後需要大量的基礎知識爲其做支撐。同樣在編寫程序,程序的好與壞標誌着程序員的能力。有兩個程序員,實現具有相同功能的程序,兩個人都完成了程序的編程工作。但是,一個人的程序代碼冗長、複雜難懂、性能低下;另一個人的程序代碼邏輯清晰、簡單明瞭、性能還很可觀。顯然編寫代碼的好壞可以體現出編程人員的能力,而編程人員的能力高低會直接影響程序的好壞。

        優秀的程序一定要滿足功能性、可靠性、易用性、高效性、可維護性、可移植性。這六個方面被稱作爲軟件質量六要素:

        功能性:是一般軟件程序的最低要求,即必須滿足目標功能要求。

        可靠性:又稱健壯性,程序可以穩定的運行、不崩潰、不異常。

        易用性:良好的用戶界面與使用方式。        

        可移植性:從一種計算環境移植到另一個計算機環境下的容易程度。

        維護性:程序代碼要有優秀的編碼風格、良好的命名規範。想象一下,如果你今天寫的一段代碼和昨天寫的代碼風格迥異,命名雜亂無章。明天你回過頭來看看自已寫的代碼說:“哦,這是誰寫的?我不認識它們。”這是很不好的習慣,優秀程序的可維護性,也體現在代碼的可讀性上,龐大功能的軟件代碼可能具有幾十萬行代碼,或幾百萬行代碼,甚至更多。這些代碼通常由不同的程序員來完成,如果每個人寫的代碼風格都不同,每個人寫的代碼在其他人看來都非常難懂,那這款軟件就是一款非常失敗的軟件,沒有很好的可維護性。所以我們經常見到一些年青人寫了一些很難懂的代碼然後自豪的說:“看,我寫的代碼你們誰都看不懂!”這樣的人不是合格的程序編寫者。優秀的事物都應該是簡單的、易懂的、公開的、無祕密的。

        接下來我們談談高效性。高效性又稱高性能要素。把這個要素放在六要素的最後來闡述是有原因的。我們在做程序設計和實現時,往往很容易把程序的性能忽略掉。人們通常認爲功能是重要的,而性能是不必要的,這是一個非常錯誤的觀點。性能是軟件程序的最核心的要素。你可以想象當你打開一個軟件讓它爲你做一件小事,而它幾乎運行了十分鐘才把你想要的照片顯示出來,這時你的心情會是什麼樣?當然,你可能認爲這個例子比較極端,但是假設我們要在全國十幾億人口中所有的一寸照中找到某一個指定人的照片時,十分鐘就好像不是那麼久了,但是如果讓你來編寫這個程序,你能讓程序多久才能完成這個功能?所以說性能是衡量軟件程序質量一個非常重要要素。

 

        數學——科學的女王,是科學界的母體。想要學好編程,同樣要學好數學,這是沒有爭議的。而人們通常所說的“寫代碼”從嚴格意義上講不能算做是編程,編程是一門很複雜的藝術。提到編程就要提及算法,因爲算法是編程的靈魂,一個沒有算法的程序是很初級的,是很蒼白無力的。優秀的程序要以合理的數據結構爲根基,加以高效的算法,用簡單漂亮的代碼實現目標功能。功能就像計算機領域裏的“麪包和水”,算法好比計算機領域裏的“金錢”。我們爲了維持生命,需要的是食物,需要吃麪包和喝水,而現代社會中我們都不會直接追求食物,而是間接的追求“金錢”,然後再用“金錢”購買“麪包和水”。當然,這個比喻並不十分恰當,但也說明了算法的重要性。

 

        我們再來談談編程語言。我不想用所謂的“高級語言”、“面向過程”、“面向對象”、“腳本語言”等等這樣的詞來闡述。在這裏我只把這些編程語言分爲兩類:傳統語言(彙編、C語言等)和流形語言(C++、Java、Python等)。目前編程語言非常多,它們通常都是多樣化、模塊化、功能化、抽象化的。我們來做一些比較:

        一、從性能上講傳統語言可以直接編譯爲CPU可識別的機器碼,從而高效的運行。而流行語言通常是運行在一個通用的平臺上的,例如Java虛擬機和.NET環境。

        二、從可移植上講傳統語言可以通過條件編譯生成不同環境下能夠運行的機器碼,而流行語言大多不需要針對系統環境修改代碼,但需要針對運行平臺做開發。

        三、從可維護性上講傳統語言同樣採用條件編譯來實現不用的功能分支及擴展,無用的代碼不會生成機器碼,更不會執行。而流行語言中爲了可擴展,常常採用很多的配置文件、配置腳本來實現功能擴展和功能切換,採用所謂低偶合的設計模式,用大量的代碼來實現可擴展性,於是流行語言的代碼通常臃腫不堪,運行緩慢,配置文件更是複雜難懂。這與優秀的程序設計理念背道而馳。

 

        我們再來引用Linux之父Linus在批評C++時說的話:

        “C++會導致非常非常糟糕的設計選擇。總是用語言的‘漂亮的’庫特性比如STL、Boost等等,這可能對程序有所‘幫助’。當這些庫無法工作時,你會有無窮無盡的折磨,這是非常低效的抽象編程模型。也許在兩年之後你纔有可能注意到有些抽象效果其實並不怎麼樣。但是,所有代碼已經依賴於圍繞它設計的‘漂亮’對象模型了,如果不全部重新設計、修改程序代碼,這些問題就無法改正。另外,我認爲字符串處理是C++會找來大麻煩的地方之一。糟糕的程序員會這樣寫代碼:

        a= b + "/share/" + c + serial_num;

        其中你肯定無法弄清到底分配了多少內存,因爲有類型強制轉換、重載的操作符,而當這種東西出現在內循環中,結果將是性能上的大災難,而且原因還不明顯!”

        引用以上內容並非斷章取義,C++與其它流程語言一樣,它們有豐富的類庫和函數庫,就像大樹的每一個分支都長滿了美麗的葉子,每一個方向都有成型類庫和函數庫。使得很多人形成“拿來主義”的思維定式。想要實現一個功能,就找到這個功能對應的庫,調用一個函數或方法就可以立即實現這個功能,這很好,也正是目前流行語言之所以“流行”的原因。然而性能問題、安全問題等等全都是隱患。當然,也可以修改開源類庫的源代碼,但畢竟能夠做的工作十分有限,而且是否能真正解決問題也不能保證。

        優秀的程序開發者要了解並掌控自己寫下的每一行代碼,明白它們的意義和功能,並能隨時隨地做任何功能上的調整。你絕不會這樣說:“這個功能是底層庫實現的,我不關心它是什麼,更不關心它是怎麼實現的。”當然,使用C語言編程也同樣需要調用大量的標準C庫函數。但優秀的程序員一定會明白這些函數內部是如何實現的,也可以自己重新編寫一個函數來實現它。

        使用流行語言“寫代碼”就像我們常年使用計算器之後再也不會口算;每天用拼音打字之後卻提筆忘字。實際上,任何領域都是一樣的,高級的工具用的久了就會忽略這句重要話:“最原始的方法往往是最有效、最靈活的方法——老把式纔是真把式。

        編程不是簡單的“寫代碼”,想要編寫出優秀的程序,自己時時刻刻都要思考:你需要使用什麼樣的數據結構,需要如何設計高效的算法。養成通過思考來解決問題的習慣,以不變應萬變。

        編程是一門藝術。這門藝術不是看了一本編程語言書之後,就能做得出色的。會寫毛筆字的人就一定是書法家嗎?會素描的人就一定是畫家嗎?學會了圍棋的規則的人就一定是圍棋大師嗎?不見得。在這裏我沒有諷刺和貶低任何人的意思。我只是說,編程與其它學科藝術一樣,想要在任何一個領域裏做的好,都需要多年的學習,實踐,積累,總結。學了一門編程語言之後只能說是剛剛入門,要想做得好,還需要學習大量的知識,通過不斷的努力,才能在編程方面有所成就。有很多人想法非常多,也非常好,很有想象力,但缺乏實踐經驗。在討論問題時侃侃而談,但從不喜歡手動去做,嘴上描述的天花亂墜,但從不腳踏實地的去寫幾行代碼。想一千次,不如去做一次。一次華麗的跌倒勝過一千次無謂的徘徊。

 

        20世紀初,美國福特公司的一臺電機出了毛病,爲了減少損失,需要在不停機的情況下把毛病找出來。公司裏的技術人員怎麼也找不出毛病在哪兒,最後,只好到外面請來了流落到美國的原德國技術專家斯坦門茨。斯坦門茨在電機房躺了三天,聽了三天,要了一架梯子,一會兒爬上去,一會兒爬下來,最後在馬達的某一個部位用粉筆畫了一道線,並說:“打開電機,在記號處把裏面的線圈減少16圈。”人們照辦後,毛病果然消除了。斯坦門茨的解決辦法非常簡單,但在這簡單的背後卻有他非常多的知識和經驗做理論支撐。

        編程的過程也是一樣,一些簡單的表象背後有着很多的知識。請參見《技巧:四捨五入》《技巧:數組的下標》《技巧:遞歸》

        所以我們說,在編程的背後有着非常多的知識和經驗做理論支撐。編程並不簡單,在編程的深處有着許許多多需要我們學習的知識,有着太多太多需要印在心裏的寶貴經驗。

         最後,讓我們用一種很有趣的方式來描述編程的藝術。

       金庸先生的武俠小說中有一位絕頂高手——劍魔獨孤求敗。他蕩盡仇寇,敗盡英雄,天下更無抗手,生平求一敵手而不可得,自述人生經過了四種境界:

        無名利劍:凌厲剛猛,無堅不摧。弱冠前以恃之與河朔羣雄爭鋒。用流行語言,一行代碼爲你完成大量複雜的工作,簡單的代碼就可以實現龐大而漂亮的功能。

        紫薇軟劍:三十歲前所用,誤傷義士不祥,乃棄之深谷。當你沉迷於流形編程語言的類庫豐富、功能強悍時,往往會出現性能問題、功能性問題,而且終究無法很好的解決,扔了它吧。

        玄鐵重劍:重劍無鋒、大巧不工,四十歲前恃之橫行天下。學習C語言和使用C語言很有難度。想要用C來寫一個有實用性的軟件程序可能要消耗掉大量的時間和精力,但使用“重劍”的過程中,你的“內功”也在飛速的提升。當你用C寫的程序從各個方面都能夠與其它流形語言所開發的程序相比肩的時候,也就是你舉重若輕的時候,此時你所編寫的程序一定好得多。

        木劍:四十歲後,不滯於物,草木竹石均可爲劍,自此精修,漸進於無劍勝有劍之境。編程語言只是我們使用的工具,當你的理論與實踐真正達到一流高手的境界時,不再侷限於任何的編程語言,也不再侷限於爲哪個領域做開發。用你的想象力和豐富的經驗爲根基,不再拘泥於任何的編程工具與軟硬件環境!給你一臺計算機,你能用它創造一個嶄新的世界!


歡迎關注公衆號:編程外星人

發佈了42 篇原創文章 · 獲贊 190 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章