专业程序员必知必会技巧:驯服复杂代码
??? ? ?? 感悟:雖然不想做一個程序員,但還是自做這些事情!藝術家首先是工匠!多美妙的哲理,可是路途漫長而很多人選擇了放棄,我也想放棄了。更重要的是,如果希望是一個藝術家,首先是一個NB的工程師。
??????? 原文鏈接:http://blog.csdn.net/hzbooks/article/details/8646593
你從入職第一天起就要應對復雜代碼。
若是還未遇到過無法理解的程序,那說明你編程的年頭還不夠長。在行業里,要不了多久你就會碰到讓人發懵的混亂代碼:巨獸、面條工廠、來自地獄的遺留系統。我曾接手過一個程序,它的前任在聽說要增加一個分量不輕的新特性時,選擇了辭職——辭職——辭職.......。(我并不怪他。)
軟件系統的復雜度是不可避免的。有些問題就是很難,它們的解決方案很復雜。然而,你在軟件中找到的大多數復雜度是我們自己造成的。在《The Mythical Man-Month》(人月神話)[Bro95]里,Fred Brooks將復雜度的兩個來源分成必然(necessary)復雜度和偶然(accidental)復雜度。
這里有一種區分必然復雜度和偶然復雜度的思考方法:什么復雜度是問題域固有的?假設你面對的是一個日期/時間處理代碼散落各處的程序。在處理時間時,存在一些必然復雜度:每月的天數不同,必須考慮閏年,等等。但多數我碰到的程序充斥著大量與處理時間相關的偶然復雜度:用不同格式保存的時間,加減時間的新奇(同時也是充滿Bug的)方法,不一致的時間打印格式,說都說不完。
復雜度的死亡螺線
編程時常會遇到這種情況:產品代碼庫中的偶然復雜度漸漸壓倒必然復雜度。情況在某一時刻會自我放大,我稱這種現象為復雜度的死亡螺線,如圖1所示。
圖1 復雜度的死亡螺線
問題1:代碼規模
構建產品時,它的代碼規模最終將遠超任何在學校或消遣項目中所遇到的。行業中的代碼庫的度量結果從成千到上百萬代碼行(Line of Code, LOC)不等。
John Lions在《Lions’ Commentary on UNIX 6th Edition》一書中寫道:單個程序員能夠理解和維護的程序大小的實際限制規模是1萬行代碼。于1975年發布的UNIX第6版的規模大約是9000行代碼(不算機器特定的設備驅動程序)。
相比而言,Windows NT在1993年有4百萬~5百萬行代碼。10年后,Windows Server 2003配備了2000名開發人員和2000名測試人員,他們管理多達5千萬行代碼。大多數行業項目并不像Windows那樣巨大,但它們也都輕易地跨過了Lions設定的1萬行代碼的警戒線。這樣的規模意味著公司內部沒有人能理解整個代碼庫。
問題2:復雜度
隨著代碼規模的增長,最初想法的概念優雅性消失了。曾經對于車庫中兩個小伙水晶般清澈的想法變成了大批開發人員艱難跋涉其中的陰暗沼澤。
復雜度并不是代碼規模的必然產物。大型代碼庫完全有可能被拆分成許多模塊,其中每個模塊都有清晰的用途、優雅的實現和為人熟知的與鄰近模塊的交互。
然而,即使設計良好的系統也會在它們變大時變得復雜。一旦沒有一個人可以理解整個系統,這時多個人必須去理解系統中自己那部分—且沒有人的理解跟其他人是完全一樣的。
問題3:Bug
產品復雜度飆升,Bug也就不可避免地出現了。這是注定的—就算是偉大的程序員也不是完人。但每個Bug并非生而平等:高度復雜系統里的那些Bug尤其難覓蹤跡。總是聽到程序員說:“真搞不懂,伙計,系統剛剛崩潰了。”歡迎來到這糟糕的調試世界!
問題4:快速修補
問題并不在于產品是否有Bug—它肯定有,關鍵在于工程團隊在出現Bug之后如何響應。在推出產品的壓力之下,大多數程序員經常求助于快速修補。
快速修補是給問題打補丁,而非解決其根本原因。甚至常常不尋找根本原因。
程序員:在試圖往網絡隊列中放入一個任務(job)且隊列在10秒內無響應時,程序崩潰了。經理:重試隊列操作100次。
根本原因是什么?天知道,只要重試次數夠多,你就可以掩蓋任何問題。但如車身修補一樣,某一位置的霸道膠水(Bondo)比實際殘留的車本身部件還要多。
更難找的問題發生在補丁并沒有解決問題根本原因的時候,問題通常根本沒有消失—它只是轉移到別處。在前面的對話中,重試100次可能很好地掩蓋了問題,但萬一需要101次重試怎么辦?經理只是隨便捏了個數字,這種膏藥式修補只會讓問題更難查。
沿著“快速修補”上行,我們現在得到了一個增加代碼規模的完整閉環。
走向清晰
提起復雜的反面,人們通常會想到簡單。但由于領域的必然復雜度,我們并不是總能寫出簡單的代碼。應對復雜更好的方法是清晰。你是不是明白自己的代碼要做什么?明確兩點會有助于我們減少軟件偶然復雜度:清晰思考和清晰表達。
清晰思考
在分析問題的原因時,我們試圖做出像“保存時間的方式應該只有一種”這樣的清晰陳述。那為何UNIX C代碼里還混雜著像time_t、struct timeval和struct timespec這樣的結構呢?那并不是太清晰。
如何調和你的清晰陳述和UNIX計時功能的復雜度?你需要隔離復雜度,或將其抽象到單個模塊中。在C里,這可能是結構體和操作它的函數;在C++里,它會是一個類。模塊化設計讓程序的其余部分可以用一種清晰的方式推導時間,而不用了解系統計時功能的內部機制。
一旦能將時間作為程序的一個單獨模塊進行對待,你也就能證明你的計時機制的正確性。完成這一工作的最佳方式就是單獨測試,但是同行評審或書寫規格說明也行。當一組邏輯是隔離的而不是內嵌在一大段代碼體內時,它的測試和嚴格證明要容易得多。
清晰表達
隨著你清晰地思考模塊并將它與其余程序隔離,最終程序也就能更清晰地表達它的用途。處理問題域的代碼應該真正專注于問題域。
將輔助代碼抽出放入自己的模塊之后,剩余邏輯讀起來應該越來越像問題域的規格說明(雖然有更多分號)。
讓我們看看前后對比。我已經無數次看到過如下這種C++代碼:
<span style="font-family:Times New Roman;">void do_stuff_with_progress1() { struct timeval start; struct timeval now; gettimeofday(&start, 0); // 干活,每半秒鐘打印一條進度消息 while (true) { struct timeval elapsed; gettimeofday(&now, 0); timersub(&now, &start, &elapsed); struct timeval interval; interval.tv_sec = 0; interval.tv_usec = 500 * 1000; // 500ms if (timercmp(&elapsed, &interval, >)) { printf("still working on it...\n"); start = now; } // 干活…… } }</span>循環的關鍵是“干活”部分,但在實際干活之前有20行的POSIX計時代碼塊。這并沒有什么不對,但……就沒有一種方法讓循環保持對其問題域而不是對計時的關注嗎?
讓我們把所有時間代碼放入它自己的類:
<span style="font-family:Times New Roman;">class Timer { public: Timer(const time_t sec, const suseconds_t usec) { _interval.tv_sec = sec; _interval.tv_usec = usec; gettimeofday(&_start, 0); } bool triggered() { struct timeval now; struct timeval elapsed; gettimeofday(&now, 0); timersub(&now, &_start, &elapsed); return timercmp(&elapsed, &_interval, >); } void reset() { gettimeofday(&_start, 0); } private: struct timeval _interval; struct timeval _start; }; </span>我們現在可以簡化循環了:
計算機在上述兩種情況下做的事情是相同的,但考慮第二個例子對程序可維護性帶來的影響:
- Timer類的測試和證明可獨立于它在程序中的使用方式。
- “干活”循環內的計時有了有意義的語義—triggered()和reset(),而不是一堆獲取、增加和比較函數。
- 現在對于計時的終止位置和(捏造的)循環實際起始位置都清晰了。
當工作在巨大丑陋的代碼上時,依次考慮:這段代碼想表達什么含義?它有沒有辦法說得更清楚一點?如果它是清晰表達的問題,你需要把那些礙事的代碼段抽象出來,同前面展示的Timer類一樣。若代碼還是有點混亂,那可能是沒有清晰思考的產品,需要在設計層面返工。
行動指南
聚焦于可被隔離和嚴格推導的一個編程方面,如計時。挖掘你正在從事的項目,識別出這樣的代碼段:若那部分邏輯被抽象到自己的模塊,它能否表達得更清晰。
動手嘗試更模塊化的方法:選一組混亂的代碼,分離必然復雜度和偶然復雜度。在這一刻不要操心細節,只看如何可以清晰地表達必要的業務邏輯,假設你有獨立模塊來處理支撐邏輯。
------------------------------------
本文節選自《程序員修煉之道:專業程序員必知的33個技巧》“技巧4:馴服復雜度”。
書名:《程序員修煉之道:專業程序員必知的33個技巧》
原書名:New Programmer's Survival Manual: Navigate Your Workplace, Cube Farm, or Startup
作者:Josh Carter
譯者:胡鍵
頁數:212
定價:49.00元
ISBN:9787111411642
豆瓣收藏:http://book.douban.com/subject/21323647/
PDF下載:http://vdisk.weibo.com/s/paFl8
當當購買:http://product.dangdang.com/main/product.aspx?product_id=23185207
內容簡介:
這是每一位致力于成為專業程序員的軟件開發新手都應該閱讀的一本書。它是資深軟件開發專家Josh Carter 20余年編程生涯的心得體會,從程序員成長的視角,系統總結和闡述了專業程序員在專業技能、編程工具、自我管理、團隊協作、工作態度以及需要采取的行動等方面應該掌握的33個非常重要且實用的技巧。作者以自己以及身邊的同事積累下來的經驗、犯過的錯誤為素材,旨在為新人們引路,讓他們在能力修煉的過程中少走彎路!
全書分為四個部分:第一部分(技巧1~14),從編程技能和工具使用兩個方面總結了14個技巧,包含如何正確地書寫代碼、測試驅動設計、管理代碼復雜度、改善遺留代碼、代碼評審、開發環境優化、自動化等;第二部分(技巧15~24),從自我管理和團隊協作兩個方面總結了10個技巧,包括如何樹立自我形象、壓力管理、建立良好人脈和高效會議等;第三部分(技巧25~30),介紹了典型高科技公司的組織結構以及你在整個公司中的位置,并且闡述了薪酬分配的問題;第四部分(技巧31~33),介紹了在日常工作中如何持續改善自己的工作和學習狀態。
作者簡介:
Josh Carter,資深軟件設計師,具有超過20年編程行業從業經驗。熱衷于編程和追逐前沿技術,但同時謹記史蒂夫?喬布斯的箴言“真正的藝術家能讓產品面市”。他還涉足工程管理領域,曾經主管大型企業軟件開發團隊。目前已出版多本關于計算機軟件的技術書籍,同時他還在主流計算機雜志的技術專欄發表文章。總結
以上是生活随笔為你收集整理的专业程序员必知必会技巧:驯服复杂代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OpenCV、OpenCL、OpenGL
- 下一篇: 2013腾讯实时面试记录