導航:首頁 > 程序命令 > 程序員重構水管圖

程序員重構水管圖

發布時間:2025-04-23 08:43:45

『壹』 為什麼國內程序員都很少進行代碼重構

說到代碼的重構對於國外的程序員提到的比較多,特別是大型的開源工程,基本上一個模塊或者函數的實現會反復的修改,一個文件能被修改成千上萬次,曾經訂閱了linux內核組的郵件,每天的收到的修改文件成千上萬,有時候一個文件都能被修改上百次,對於文件修改最瘋狂的是google的chrome源碼,重構的次數,讓你覺得每天都在重寫但是功能上感覺越來越流暢。為什麼我們周圍的程序員絕大部分時間做的不是這樣的事情。

為啥從直覺上覺得老外的寫的代買質量比我們的要高,我們國內的程序員絕大部分的時間是在趕進度,准確的來講忙著增加功能和修改bug,其實也從側面反映出為什麼國內出不了android以及Linux等影響深遠的 科技 創新,從全球開源代碼的佔比就可以看出,差距還是很巨大的。

為什麼覺得老外寫的代碼比我們的強?

1.國內軟體發展主要階段還在解決有沒有,還遠談不上強大
中國的軟體經過近幾十年長足的發展,已經取得了巨大的成就,特別在互聯網行業已經有幾個巨頭躋身世界前列了,最近炒的很熱的臉書的用戶數據泄密事件,作為當事人扎克伯格,也在論述中提到中國有幾個很厲害的互聯網公司,這說明中國在互聯網領域還是取得了相當大的成就,但是在一些核心的領域,或者門檻很高的領域差別還是非常巨大。

任何事情在發展的初級階段首要考慮的是不是有沒有,所以如同創業初期的公司會選擇短時間內搞出來個產品,哪怕是不成熟的產品,然後快速的投入市場,根據市場用戶的反應同步追蹤問題,等到產品差不多穩定,並且產品在市場上有了一席之地之後,後續的事情就要考慮優化功能,對裡面的代碼或者產品的性能進行全方面的提升,目前國內大部分的互聯網一般比較年輕,還在解決有沒有的問題,相信隨著時間的推移以及國內軟體的發展,也會有大量的高質量的開源框架代碼出來,但這一切都需要很長的時間。

所以國內的程序員大部分時間都是在趕進度和根據需求完成功能代碼。

2.軟體產業的底子還很薄弱, 歷史 積淀還不夠
舉個很典型的例子,現在很多國內的程序員到了30多歲就開始考慮後續的轉型了,因為後面的輕輕人會帶來很大的沖擊,所以大部分的30多歲的程序員都在考慮自己後路,都要考慮轉型的問題。老的有經驗的程序員反而轉型去做管理或者合夥創業了,哪有幾個還在安心搞技術,年齡大了還在搞技術的還被人鄙視,覺得自己沒有出息。

但是在國外寫代碼是一種很常見的職業,和別的工種沒有多大的差異,40,50歲了寫代碼也是比比皆是,做軟體是一種技術工種,經驗的佔比是很高的,所以老程序員寫出來的代碼更加有深度,穩定性更高,一切的根源還是產業的發展不夠成熟,需要時間和 歷史 的積淀,從這方面講國內的軟體整體產業還是比較薄弱,從業人員的整體素質和工作氛圍還有待慢慢的成熟,周圍都是有經驗的程序員在帶領著如何去重構代碼,如何提升代碼的質量,而國內大部分的程序要還是被產品經理鞭策著增加需求和修改代碼。

3.公司的文化差異
目前很多的中國技術公司更多的追求的是短期利益的最大化,在基礎軟體的投入遠遠不夠,畢竟基礎的投入很難短期見成效,在一個具體的場景,有一個產品主體的功能已經實現了,也能在用戶那邊投入使用了,一般的公司很難拿出時間來,讓你做代碼的重構,畢竟這種事情很難直接產生經濟效益。這與公司本身的文化差異有很大的關系,重視的技術或者懂得技術的公司對於這方面相對比較重視,反之就差很多。

小時候課本上就說著我們落後100年,所以高樓大廈不是一天建成的,所以在追趕的道路很漫長,所以承認存在差距,然後努力加倍的去追趕。

Time is money. 以目前國內互聯網的情況,需求應接不暇,程序員基本上都是被需求與業務趕著走,時間非常緊張,在這種情況下,程序員很多時候唯一的選擇就是趕緊實現需求的功能。所以,一個項目下來,代碼基本上都變得非常非常的「垃圾」。

也有很多程序員想過在項目結尾的時候進行代碼的重構,基本上每個程序員也都知道重構代碼的好處,但是並不代表著真正能做起來。還是那個原因,國內互聯網的速度太快,需求應接不暇,做為程序員,基本上沒有時間來做這件事情。

而另外一個原因是跟團隊負責人有關。若團隊負責人能夠意識到重構的好處,那麼他可以為此單獨劃分一段開發時間出來,讓大家分別負責一個模塊進行重構,這都是可以安排做起來的。這也需要團隊負責人如何在需求人員的需求與代碼質量的進度上做一個平衡,進行統籌安排。

最後我想說一個可能很少意識到的原因,那就是人員流動問題。國內互聯網目前人員流動非常的大,尤其是北上深這樣的互聯網發達的城市,基本上是平均兩年就會走一大波人,在這樣的情況下,也會考驗從業人員的職業道德,即我到底要把代碼寫的多好,要把代碼的可維護性做到多好,其實這都是從業人員自身需要考驗的問題,因為完成一個功能很容易,但是要考慮的全面就是另外一回事了。而人員流動帶來的另一個問題就是有一些代碼是很難看懂的,即有些代碼在人員離開後成為了「 歷史 」,無人敢動。這也會阻礙著軟體的重構工作的進行。

從我所講的這幾種情況來看,重構其實是大家都能知道的好處,但是真正實施起來卻又有現實的約束,需要負責人來做這樣的統籌安排與推動。

成本太大
大多數軟體產品的開發都是經過了很多開發人員的付出,如果進行代碼重構需要了解產品、了解框架、了解代碼邏輯,這個過程會花費大量的時間和人力成本,對於企業來說,效益是第一位的,與其花費精力進行效益不大的產品重構,不如去承接更多的項目來的實在。
領導決策
由於大部分企業的老闆都是非技術人員出身,他們更關注效益和客戶,為了符合企業的發展,在進行產品開發時會更多的採用新技術來吸引客戶,花費精力重構代碼不如開發一套新產品或者開發更酷炫的效果更具有實在意義。
代碼規范不足
由於國內互聯網較之國外起步較晚,很多企業發展時間較短,加之人員流動比較大等多方面的因素,很難形成標准、嚴謹、行之有效的代碼規范,所以很多技術人員在開發時都是根據個人風格習慣在開發,等其他人接手代碼時,缺少相關標准和文檔,很難理解代碼邏輯,花時間去了解代碼、重構代碼不如直接推翻重做來的方便。
客戶定製化需求
部分企業創業初期對企業信息化是不夠重視的,只有企業發展到一定程度才會考慮信息化建設,而由於業務的限制,大多數標准化的互聯網產品都很難滿足企業的實際需求,需要進行大量定製化的開發,對於互聯網企業來說,即使產品開發足夠完善,在實際項目中也需要進行擴展,倒不如直接在項目中進行調整。
程序員的發展限定
在國內很少有終身的程序員,大部分都會逐漸轉向銷售、售前、項目經理、產品經理等崗位,而這些崗位則需要了解業務、了解客戶,對技術的需求反而不會太高,所以與其花時間去專研技術不如將更多的精力用在業務和項目層面。

代碼能夠重構對底層框架要求深度掌握、且代碼框架本身要足夠靈活,而國內絕大部分技術人員都是停留在對框架的使用層面、少數可以完善、結合使用,極個別的在做同語言山寨或者換一種語言重寫,能夠對產品體系進行把握、與時俱進擴展實在是鳳毛麟角。隨著國家的經濟提升、IT行業逐漸成熟,在我國這么多IT公司基數下,即便是鳳毛麟角的概率,重視基礎框架、積累萃取、不斷迭代完善的一些技術公司也會慢慢嶄露頭角、涌現出來的。

【國內程序員很少進行代碼重構】,這個現象雖然沒有什麼調查統計,不過我寫了十多年代碼,也發現身邊的程序員大多數是這樣的,【寧可寫新的代碼,也不願意重構老代碼】。下面我也談談自己的看法:

系統沒有問題,就是最大的功勞
我見過的大部分的傳統行業的軟體公司或IT部門是這樣的(互聯網公司不太了解),「只要系統穩定,那麼就是最大的功勞」,而保持系統穩定最好的方法是什麼? 就是盡可能的不要動系統!

可能很多人不能理解,但很多公司確實是這樣,甚至公司對項目的考核標准中,項目有什麼突破的權重很低,是否有生產事故的權重很高。所以很多「機智」的項目組成員,千方百計的不接需求,或者把需求推給別的項目組。在這種單位裡面,別說重構了,新代碼都寫的不多。

測試覆蓋度太低,重構代碼沒辦法保證質量
代碼重構,很重要的一個問題:「重構後的代碼誰來保證?如果影響到原有的功能怎麼辦?」

這時候很有效的一個方法,是使用各種自動化的測試來保證重構代碼的質量。

但是,大部分公司,不管是單元測試還是其他的自動化測試,都是不健全的,甚至是沒有的。所以只要不是被逼不得已,程序員寧可重新寫一個方法,也不願意重構之前的代碼。

其他

進行代碼重構不是一件容易的事情,務必需要對需求熟悉;對代碼 歷史 變更熟悉;對代碼框架,模塊熟悉;對產品更新迭代做好風險把控,時間成本把控……

進行代碼重構需要能力非常高,責任心非常強的人進行,甚至需要一個優秀的團隊完成。

為什麼要代碼重構?理由一大堆,我認為主要有兩條,一是原代碼已不適合擴展新需求,二是原代碼已擁腫不堪,亂七八糟。

為什麼很少重構?除了上述分析外,還有其他因素,如人員流動快,原團隊原作者早已不知何去何從了。又如需求和業務繁多,完成工作開發都累得半死不活,日理萬機似的,哪有時間和心情重構?

謝謝大家。

1.國內程序員技術能力不足以進行代碼重構

大量的軟體從業人員連編程規范都不熟悉,怎麼可能做代碼重構?更多的人只會寫寫hello world,只會拷貝粘貼小段代碼,連if else這種語句都寫不清楚甚至漏掉邏輯,連面向對象的編程思想都沒有,談何重構?

2.國內程序員的溝通能力說服能力一般。

進行軟體重構,必須說服經理,讓經理相信重構會帶來軟體質量的提高和故障率的逐步降低,這樣經理才會安排人力進行重構。

3.國內軟體開發更注重bug的及時解決

國內軟體開發大量的人力被分配到解決短期的某個bug,沒人抽時間思考如何長久的徹底的解決軟體缺陷,其實解決bug不重要,找到軟體的缺陷或者性能低下的地方才重要,這些才是重構的點。國內加班加點疲於奔命式的開發,沒人考慮bug率是否長期內能夠收斂,總是先解決眼前的問題再說,處於一個永遠解決bug的死循環里。

這種工作模式是愚蠢的,不是smart的。

軟體開發,一定要動腦子,不要蠻干,這不是耕地,力氣大就耕的多。

重構代碼的目的說白了,就是讓軟體開發人員更自由。

謝謝樓主的問題,這是一個我特別想回答的問題?

為什麼?因為,第一,我是一個對代碼有潔癖的人,受不了一坨,一坨那樣的代碼。第二,我是一個踐行Clean Code 的人,給大家我主要負責的一個項目的一組數據(java),總代碼量20萬行,UT coverage(單元測試代碼覆蓋率)82%,代碼重復率0.5%,代碼規則(sonar)違反(Code issue)0,甚至連最低的違反都沒有。

也正是因為我的項目在實踐Clean Code上的數據,我經常去給不同的團隊做分享,也對團隊對這個重構不太上心有一些理解。

大致以下幾個原因。

第一,也是最多的,交付壓力,大部分人都會抱怨,你看我們有這么多新功能,還有那麼多bug,根本忙不過來,哪有時間重構?

第二,重構意識不足,老闆,管理人員總是希望這個我們要有,那個我們也要實現?為什麼?因為別人有,別人有我們沒有可能會造成用戶流逝。即使有一些有見識的程序員和老闆反應這個重構問題,但是重構從來不是高優先順序的。畢竟,現在的軟體的生命周期可能很短。

第三,人員流動性大,這個是我聽過最奇葩的一個理由,我問一個來聽培訓的哥們,說你代碼寫成這樣,以後怎麼維護?這個哥們說,我也知道難維護,但我明年就跳槽了。

第四,設計上就不需要重構,曾經給一個保險公司做分享,我本人也是做金融相關產品後台的,我就問你們這樣寫代碼,可能三四年以後就非常難維護了,還是要盡快重構。他們的回答是,我們不重構,我們只重寫。什麼意思那?就是一個系統,三四年以後在寫一遍。

第五,程序員本身的問題,可能第一寫單元測試,修改命名,修改代碼結構,是一件很沒有成就感的事情,也是一個沒有多少附加值的事情。畢竟現在你去找工作,這個代碼質量方面的問題會問得很少。

第六,我見過的我不能反駁的一個回答,我的英文太差,不能很好的命名,而我也不想學英文。

第七,反正我已經實現了功能。

最後,用一句話來提醒程序員們,重構是多麼重要。

出來混遲早要還的,挖了坑遲早要填的。

在國內,【重構】這件事是程序員最喜歡做的事,而不是公司喜歡做的事。

但程序員喜歡並不能影響公司的決策,所以,國內的現象就是軟體系統很少有重構。

其根本的原因在於,國內的公司所推出的系統大多沒有重構的價值。

1、國內的互聯網公司存在這樣一種快節奏,那就是發現一個有價值的創意,就馬上進入開發,開發完成立即上線,並立馬推廣使用。這是一種快速試錯的模式,一旦發現系統在 社會 中沒有引起反響,那就馬上把系統再下線。這種情況下的系統哪來的重構價值呢?

2、即使一個系統上線成功了,也積累了大量的用戶,貌似為了系統的穩定性和性能,可以有重構的機會。但事實上不是,國內公司仍舊不會選擇重構,因為重構帶不來新的價值。所以,公司多數都會在系統上添加新的功能來吸引新的客戶,而不會考慮重構現有系統。

總得來說,引起重構的原因在於能夠有持續的價值。沒有價值的事,企業當然不會做,僅是程序員的一廂情願而已。

你只是見到了你所在的公司現象,不代表所有,也不能代表大部分的。

代碼重構還是存在的現象

代碼設計爛,經常出問題、擴展麻煩、維護心累、數據混亂、結果不清醒、模塊劃分混亂

就可能要考慮到重構了

呵呵,

老大說:

你趕緊去修復一下這個bug,

還有幾個功能沒有實現,加班搞一下,

pm 說:

這個功能改一下,

還有這個,界面重新調整一下,

這個業務流,現在不一樣了,

客戶需求需要多幾個功能,

老闆說:

這東西下周能出來嗎?

『貳』 程序員的七種武器是什麼

根據本人的多年開發經驗,向那些剛剛踏入IT行業的新程序員們或正在迷茫的程序員們整理出了程序員必須掌握 的七種武器以及相關的視頻教程。 第一種武器:開發工具 基於C++、VC++開發平台工具快速入門: 第一部份: VS2008 IDE開發環境的基本使用方法 第二部份:VS2008調試環境的使用 第三部份:VS2008性能分析工具 基於MyEclipse平台的Java程序快捷開發: 第一部份:JAVA開發環境的搭建 第二部份:MyEclipse工具常用開發操作和技巧 第三部份:MyEclipse高級使用技藝 - 重構與快速實現 基於Linux操作系統平台下的Java語言開發: 第一部份: 安裝虛擬機以及Linux操作系統 第二部份: Linux桌面系統 第三部份: Linux文件和目錄管理 第四部份: VIM的使用(上) 第五部份: VIM的使用(下) 第六部份: Linux終端常用命令第七部份: Java開發環境的搭建 第二種武器:資料庫 SQL Server 2008 資料庫基礎及應用: 第一講:SQL Server的安裝與配置 第二講:SQL Server中的庫、表、數據完整性 第三講:SQL Server中的CRUD語法 第四講:SQL Server中的查詢語句 第五講:SQL Server中的高級查詢 第六講:SQL Server高級查詢綜合示例(一) 第七講:SQL Server高級查詢綜合示例(二) 第八講:SQL Server高級查詢綜合示例(三) ORACLE系列之SQL從入門到精通: 第一講:資料庫基礎知識 第二講:創建資料庫 第三講:管理和控制Oracle資料庫 第四講:Oracle資料庫表管理 第五講:基本SQL語句 第六講:對數據進行限定和排序 第七講:處理單行的函數 第八講:從多個表中獲取數據 第九講:數據查詢的綜合案例(一) 第十講:使用分組函數來對數據進行聚集 第十一講:子查詢及高級應用 第三種武器:操作系統 Linux系統編程(文件篇、進程篇、信號與管道篇、時間篇、實戰篇): 第一部分:Linux系統編程之文件篇 第二部分:Linux系統編程之進程篇 第三部分:Linux系統編程之信號與管道篇 第四部分:Linux系統編程之時間篇 第五部分:Linux系統編程之實戰篇(minishell實現) 第四種武器:網路協議TCP.IP Windows伺服器與網路編程實戰課程(VC++系列之網路編程、WinSock超基礎): 第一部份:計算機網路和網際協議(TCPIP) 第二部份:標准套接字SOCKET原理及編程 第三部份:WinSock工作模式和編程模型 第四部份:WinSock2.0API和SOCKET池 剖析.NET網路通信、音頻、移動平台IM及伺服器端編程: 第一部份:.NET網路通信了解基本的.NET網路通信基礎 第二部份:UDP點對點網路通信 第三部份:UDP伺服器/客戶端通信 第四部份:UDP網路文件傳輸 基於TCPIP協議的java多線程高並發伺服器實戰: 第1課:TCPIP協議,多線程,高並發網路編程概述 第2課:基於TCPIP協議的自定義網路通信協議實現(一) 第3課:基於TCPIP協議的自定義網路通信協議實現(二) 第4課:多線程精講(一) 第5課:多線程精講(二) 第6課:網路基礎編程(一) 第7課:網路基礎編程(二) 第8課:網路基礎編程(三) 第9課:java NIO(一) 第10課:java NIO(二) 第11課:java NIO(三) 第五種武器:DCOM.CORBA.XML.WEB Services .NET下面的WebService開發: 1.掌握 Web Service 的基本工作原理 2.理解 Web Service 的優勢 3.掌握 Web Service 的開發和使用 4.ASP.net中如何通過代理類調用WebServic 5.ASP.net中如何手工調用WebService(Get/Post兩種方式) 6.JavaScript如何同步調用WebService 7.JavaScript如何非同步調用WebService 8.ExtJs中如何調用WS 9.通過SOAP頭來增強WebService安全性 10.Web Service開發中需要注意的問題 Java平台下的WebService框架Xfire深入解析: 第一部分、WebService 概念與xFire入門 第二部分、xFire開發起步 第三部分、Web服務客戶端及Web服務安全性 第四部分、xFire與Spring集成 第五部分、xFire使用綜合示例 第六種武器:軟體工程與CMM IT人必備實用項目管理系列訂餐系統實例化教學—Java版,10年IT經驗總結,涉及測試、風險、負載等: 1. 課程綜述:我們目前項目管理中遇到的困惑和我們所處的現狀。 2. 團隊組建和項目計劃。 3. 需求管理:任何人都為之頭疼的東西。 4. 實戰訂餐系統需求管理:,體驗一把從頭構建需求的快樂和痛。 5. 軟體配置管理:只是技術,純粹解決項目開發問題。 6. 軟體設計:該怎麼去設計一個軟體。 7. 進度控制:進度是大部分項目組頭疼的事情。 8. 風險管理:我們能做到的到底有多少。 9. 成本管理:不是說如何報價,而是說如何做能合理些。 10.軟體測試:確保軟體質量的關鍵點。 11. 單元測試:如何使用junit實施單元測試。 12. 負載測試:看看這輛車到底能拉多少貨? 13. 驗收交付:客戶滿意,公司滿意,我們的追求。 14. 過程改進:下個項目我們還需要這么費力嗎? IT項目管理實戰ASP.NET版(負載均衡+Web測試+VSTS單元測試+VSS+訂餐系統,10年經驗總結): 1. 課程綜述:我們目前項目管理中遇到的困惑和我們所處的現狀。 2. 團隊組建和項目計劃。 3. 需求管理:任何人都為之頭疼的東西。 4. 實戰訂餐系統需求管理:,一起體驗一把從頭構建需求的快樂和痛。 5. 軟體配置管理:只是技術,純粹解決項目開發問題。 6. 軟體設計:該怎麼去設計一個軟體。 7. 進度控制:進度是大部分項目組頭疼的事情。 8. 風險管理:我們能做到的到底有多少。 9. 成本管理:不是說如何報價,而是說如何做能合理些。 10.軟體測試:確保軟體質量的關鍵點。 11. 單元測試:如何使用junit實施單元測試。 12. 負載測試:看看這輛車到底能拉多少貨? 13. 驗收交付:客戶滿意,公司滿意,我們的追求。 14. 過程改進:下個項目我們還需要這么費力嗎? 第七種武器:演算法與數據結構 C#版數據結構與演算法高級教程(深入探討)--附各種演算法實例: 一、演算法的評價指標 (1) 二、線性表:(3) 三、棧和隊列 (3) 四、串(3節) 五、樹(5) 六、排序(4) 實戰應用Java演算法分析與設計(鏈表、二叉樹、哈夫曼樹、圖、動態規劃、HashTable演算法): 第一講、演算法基本概述、抽象數據類型 第二講、演算法的設計目標、時間復雜度和空間復雜度 第三講、線性結構與順序表的實現與應用 第四講、單向鏈表以及單向鏈表的應用 第五講、循環鏈表模擬鏈表以及循環鏈表應用 第六講、棧的基本概念以及順序棧的應用 第七講、鏈式堆棧以及棧的應用 第八講、中綴表達式轉換後綴表達式演算法 第九講、隊列以及順序循環隊列的應用 第十講、鏈式隊列以及優先順序隊列應用

記得採納啊

『叄』 為什麼中國的程序員總被稱為「碼農」

對於一些貶義的說法,個人認為作為一個程序員應該保持「誠意開張聖聽,不要妄自菲薄」的態度。程序員一直以來看哪個是別名最多的一個職業,我姑且不分褒貶的稱之為你才吧!就像小學的時候一樣,相互之間往往喜歡區別名叫昵稱之類的,而又往往外號叫的最響小名最多的就是最受關注的哪一個,程序員在當今網路上的處境大抵如此。碼農這個詞米偶遇仔細研究過來源。參考其他人的回答知中文中的碼農大體相當於英文中的code monkey。程序員碼農說法的由來大概來自於程序員圈內自嘲的說法。這里程序員對碼農的更多理解可能更接近coder這個詞,就是說我是一個寫代碼的。可能會有人以此來明志,表面自己很熱愛寫代碼,或我很精通以此,再或者言外之意我只是一個寫代碼的,別來找我給你裝系統修電腦什麼的,我最煩這個了(ノ`Д)ノ。
程序員這個行業知名度雖然高,但是正在了解程序員是做滲坦什麼的人並不多,大多數人直觀臆想出來的感受可能是一個座在電腦前,後背前傾,頸脖前伸,面容憔悴,形容枯槁,兩眼無關緊盯著電腦,兩只雞爪子似的雙手快速的在鍵盤上敲擊,屏幕快速出現一行行一塊塊英文字元的形象。這其實只是程序員的表面,完全米有展現出大多數程序員的內在。
程序員真正的工作是解決問題,代碼只是解決問題的途徑,或曰實現方案。程序員究竟解決什麼問題呢,又是從哪裡來的問題呢?首先要提到產品經理,產品經理給成員一個需求,程序員要思考如何實現這個需求。比如產品說這個登錄過程應該這樣這樣,用戶是否有通過手機或者郵箱驗證。程序員要做的就是想方案來實現這個需求。
在比如產品說我們這個網站要同時支持多少人訪問不會出現卡,或者頁面刷不出的情況。程序員接到這個需要就要思考如何設計這樣一個高性能,高並發的服務端,最終通過代碼來實現設計。
好,現在代碼寫完啦,產品發布上線了。什麼購物網站啊,大家可以隨意挑選自己喜歡的產品,什麼交友網站啊,大家可以寫好自己的介紹發布出去讓別人看到。但是還米有完,可能這個網站還要加點新功能,或者程序員自己也想,這個代碼有沒有什麼地方實現的不好,換一個方法會不會更優雅。然後又是思考解決<=實現方案<=線下測試。自己測試發現可以,這個方法很好,發布到線上,就是用戶最終使用的形態。不斷提出新需求,完善新的功能我們稱之為迭代。改善現有設計我們稱之為重構。這些都是非常有藝術感的事!
事物發展就會演缺念變出各種變體,有一些公司會把問題和解決問題的方法都做好,然後再交給其他公司或者個人去做實現(寫代碼)。由於在這里解決問題的過程被剝離出來(最有技術和藝術感的部分)剩下的就是實現,就是敲代碼。
好比建一棟房子,房子的設計和施工方案都已經做好了,就剩下施工了,這時候只要找個施工隊就可以了。在這些做設計的公司,他們是有能力來實現這些方案(敲代碼)。
但伏喊困由於歐美日等國家人力成本高,將這些技術含量低敲代碼的苦活兒剝離出來交給相對落後地區的人去實現可以幫助他們節省人力成本。以上這個現象就稱之為軟體外包。
另一方面,在美國主導的全球生產分工下,美帝也希望將中印這樣的發展中國家固定在低端製造,勞動秘籍型的行業。
既然程序員是解決問題的,那麼是否所有電腦相關的問題程序員都能解決呢?縱向來看,計算機系統可以簡單的分為三層,應用程序<=操作系統<=計算機硬體。計算機硬體的設計研發基本和程序員無關。硬體往上就是程序員的職責范圍了。這是可以簡分為應用軟體程序員和系統軟體程序員。系統程序員的責任是實現高效的硬體管理,應用程序員則是為用戶提供高效的服務。
下面說說在這兩方面和國外的差距,手機端應用軟體(有服務端的包括服務端)差別不大,大家從自己手機中軟體就能感覺到。PC端有差距,比如人家有PS這樣的圖片處理軟體,而我們則沒有。在這方面人家發展了幾十年我們年數不夠,有差距還是可以理解的。但我輩當發奮努力,以追他人之先。
另外應用程序web化應該是趨勢,這方面我們還是有優勢的,對於普通用戶來說最直觀的體驗就是不用裝很多軟體了,只要有一個瀏覽器就行。
在操作系統層面,PC如Windows,伺服器如Linux。Windows微軟獨家擁有的閉源系統,不說。Linux內核開發來講國內正在迎頭追趕,內核的郵件中中文拼音的人名越來越常見,越來越多的國人加入到Linux內核的開發中。

『肆』 要成為一名專業的程序員,從零開始需要怎麼一步步來比較好,要把最底層的先學精通嗎(個人認為)求學長

前言
你是否覺得自己從學校畢業的時候只做過小玩具一樣的程序?走入職場後哪怕沒有什麼經驗也可以把以下這些課外練習走一遍(朋友的抱怨:學校課程總是從理論出發,作業項目都看不出有什麼實際作用,不如從工作中的需求出發)
建議:
不要亂買書,不要亂追新技術新名詞,基礎的東西經過很長時間積累而且還會在未來至少10年通用。
回顧一下歷史,看看歷史上時間線上技術的發展,你才能明白明天會是什麼樣。
一定要動手,例子不管多麼簡單,建議至少自己手敲一遍看看是否理解了里頭的細枝末節。
一定要學會思考,思考為什麼要這樣,而不是那樣。還要舉一反三地思考。
註:你也許會很奇怪為什麼下面的東西很偏Unix/Linux,這是因為我覺得Windows下的編程可能會在未來很沒有前途,原因如下:
現在的用戶界面幾乎被兩個東西主宰了,1)Web,2)移動設備iOS或Android。Windows的圖形界面不吃香了。
越來越多的企業在用成本低性能高的Linux和各種開源技術來構架其系統,Windows的成本太高了。
微軟的東西變得太快了,很不持久,他們完全是在玩弄程序員。詳情參見《Windows編程革命史》
所以,我個人認為以後的趨勢是前端是Web+移動,後端是Linux+開源。開發這邊基本上沒Windows什麼事。
啟蒙入門
1、 學習一門腳本語言,例如python/Ruby
可以讓你擺脫對底層語言的恐懼感,腳本語言可以讓你很快開發出能用得上的小程序。實踐項目:
處理文本文件,或者csv (關鍵詞 python csv, python open, python sys) 讀一個本地文件,逐行處理(例如 word count,或者處理log)
遍歷本地文件系統 (sys, os, path),例如寫一個程序統計一個目錄下所有文件大小並按各種條件排序並保存結果
跟資料庫打交道 (python sqlite),寫一個小腳本統計資料庫里條目數量
學會用各種print之類簡單粗暴的方式進行調試
學會用Google (phrase, domain, use reader to follow tech blogs)
為什麼要學腳本語言,因為他們實在是太方便了,很多時候我們需要寫點小工具或是腳本來幫我們解決問題,你就會發現正規的編程語言太難用了。
2、 用熟一種程序員的編輯器(不是IDE) 和一些基本工具
Vim / Emacs / Notepad++,學會如何配置代碼補全,外觀,外部命令等。
Source Insight (或 ctag)
使用這些東西不是為了Cool,而是這些編輯器在查看、修改代碼/配置文章/日誌會更快更有效率。
3、 熟悉Unix/Linux Shell和常見的命令行
如果你用windows,至少學會用虛擬機里的linux, vmware player是免費的,裝個Ubuntu吧
一定要少用少用圖形界面。
學會使用man來查看幫助
文件系統結構和基本操作 ls/chmod/chown/rm/find/ln/cat/mount/mkdir/tar/gzip …
學會使用一些文本操作命令 sed/awk/grep/tail/less/more …
學會使用一些管理命令 ps/top/lsof/netstat/kill/tcpmp/iptables/dd…
了解/etc目錄下的各種配置文章,學會查看/var/log下的系統日誌,以及/proc下的系統運行信息
了解正則表達式,使用正則表達式來查找文件。
對於程序員來說Unix/Linux比Windows簡單多了。(參看我四年前CSDN的博文《其實Unix很簡單》)學會使用Unix/Linux你會發現圖形界面在某些時候實在是太難用了,相當地相當地降低工作效率。
4、 學習Web基礎(HTML/CSS/JS) + 伺服器端技術 (LAMP)
未來必然是Web的世界,學習WEB基礎的最佳網站是W3School。
學習HTML基本語法
學習CSS如何選中HTML元素並應用一些基本樣式(關鍵詞:box model)
學會用 Firefox + Firebug 或 chrome 查看你覺得很炫的網頁結構,並動態修改。
學習使用Javascript操縱HTML元件。理解DOM和動態網頁(Dynamic HTML: The Definitive Reference, 3rd Edition - O'Reilly Media) 網上有免費的章節,足夠用了。或參看 DOM 。
學會用 Firefox + Firebug 或 chrome 調試Javascript代碼(設置斷點,查看變數,性能,控制台等)
在一台機器上配置Apache 或 Nginx
學習php,讓後台PHP和前台HTML進行數據交互,對伺服器相應瀏覽器請求形成初步認識。實現一個表單提交和反顯的功能。
把PHP連接本地或者遠程資料庫 MySQL(MySQL 和 SQL現學現用夠了)
跟完一個名校的網路編程課程(例如:http://www.stanford.e/~ouster/cgi-bin/cs142-fall10/index.php ) 不要覺得需要多於一學期時間,大學生是全職一學期選3-5門課,你業余時間一定可以跟上
學習一個javascript庫(例如jQuery 或 ExtJS)+ Ajax (非同步讀入一個伺服器端圖片或者資料庫內容)+JSON數據格式。
HTTP: The Definitive Guide 讀完前4章你就明白你每天上網用瀏覽器的時候發生的事情了(proxy, gateway, browsers)
做個小網站(例如:一個小的留言板,支持用戶登錄,Cookie/Session,增、刪、改、查,上傳圖片附件,分頁顯示)
買個域名,租個空間,做個自己的網站。
進階加深
1、 C語言和操作系統調用
重新學C語言,理解指針和內存模型,用C語言實現一下各種經典的演算法和數據結構。推薦《計算機程序設計藝術》、《演算法導論》和《編程珠璣》。
學習(麻省理工免費課程)計算機科學和編程導論
學習(麻省理工免費課程)C語言內存管理
學習Unix/Linux系統調用(Unix高級環境編程),,了解系統層面的東西。
用這些系統知識操作一下文件系統,用戶(實現一個可以拷貝目錄樹的小程序)
用fork/wait/waitpid寫一個多進程的程序,用pthread寫一個多線程帶同步或互斥的程序。多進程多進程購票的程序。
用signal/kill/raise/alarm/pause/sigprocmask實現一個多進程間的信號量通信的程序。
學會使用gcc和gdb來編程和調試程序(參看我的《用gdb調試程序》)
學會使用makefile來編譯程序。(參看我的《跟我一起寫makefile》)
IPC和Socket的東西可以放到高級中來實踐。
學習Windows SDK編程(Windows 程序設計 ,MFC程序設計)
寫一個窗口,了解WinMain/WinProcere,以及Windows的消息機制。
寫一些程序來操作Windows SDK中的資源文件或是各種圖形控制項,以及作圖的編程。
學習如何使用MSDN查看相關的SDK函數,各種WM_消息以及一些常式。
這本書中有很多常式,在實踐中請不要照抄,試著自己寫一個自己的常式。
不用太多於精通這些東西,因為GUI正在被Web取代,主要是了解一下Windows 圖形界面的編程。@virushuo 說:「 我覺得GUI確實不那麼熱門了,但充分理解GUI工作原理是很重要的。包括移動設備開發,如果沒有基礎知識仍然很吃力。或者說移動設備開發必須理解GUI工作,或者在win那邊學,或者在mac/iOS上學」。
2、學習Java
Java 的學習主要是看經典的Core Java 《Java 核心技術編程》和《Java編程思想》(有兩卷,我僅鏈了第一卷,足夠了,因為Java的圖形界面了解就可以了)
學習JDK,學會查閱Java API Doc Java Platform SE 6
了解一下Java這種虛擬機語言和C和Python語言在編譯和執行上的差別。從C、Java、Python思考一下「跨平台」這種技術。
學會使用IDE Eclipse,使用Eclipse 編譯,調試和開發Java程序。
建一個Tomcat的網站,嘗試一下JSP/Servlet/JDBC/MySQL的Web開發。把前面所說的那個PHP的小項目試著用JSP和Servlet實現一下。
3、Web的安全與架構

學習HTML5,網上有很多很多教程,以前酷殼也介紹過很多,我在這里就不羅列了。
學習Web開發的安全問題(參考新浪微博被攻擊的這個事,以及Ruby的這篇文章)
學習HTTP Server的rewrite機制,Nginx的反向代理機制,fast-cgi(如:PHP-FPM)
學習Web的靜態頁面緩存技術。
學習Web的非同步工作流處理,數據Cache,數據分區,負載均衡,水平擴展的構架。
實踐任務:
使用HTML5的canvas 製作一些Web動畫。
嘗試在前面開發過的那個Web應用中進行SQL注入,JS注入,以及XSS攻擊。
把前面開發過的那個Web應用改成構造在Nginx + PHP-FPM + 靜態頁面緩存的網站
4、學習關系型資料庫
你可以安裝MSSQLServer或MySQL來學習資料庫。
學習教科書里資料庫設計的那幾個範式,1NF,2NF,3NF,……
學習資料庫的存過,觸發器,視圖,建索引,游標等。
學習SQL語句,明白表連接的各種概念(參看《SQL Join的圖示》)
學習如何優化資料庫查詢(參看《MySQL的優化》)
實踐任務:設計一個論壇的資料庫,至少滿足3NF,使用SQL語句查詢本周,本月的最新文章,評論最多的文章,最活躍用戶。
5、一些開發工具
學會使用SVN或Git來管理程序版本。
學會使用JUnit來對Java進行單元測試。
學習C語言和Java語言的coding standard 或 coding guideline。(我N年前寫過一篇關C語言非常簡單的文章——《編程修養》,這樣的東西你可以上網查一下,一大堆)。
推薦閱讀《代碼大全》《重構》《代碼整潔之道》
高級深入
1、C++ / Java 和面向對象
我個人以為學好C++,Java也就是舉手之勞。但是C++的學習曲線相當的陡。不過,我覺得C++是最需要學好的語言了。參看兩篇趣文「C++學習信心圖」 和「21天學好C++」
學習(麻省理工免費課程)C++面向對象編程
讀我的 「如何學好C++」中所推薦的那些書至少兩遍以上(如果你對C++的理解能夠深入到像我所寫的《C++虛函數表解析》或是《C++對象內存存局(上)(下)》,或是《C/C++返回內部靜態成員的陷阱》那就非常不錯了)
然後反思為什麼C++要干成這樣,Java則不是?你一定要學會對比C++和Java的不同。比如,Java中的初始化,垃圾回收,介面,異常,虛函數,等等。
實踐任務:
用C++實現一個BigInt,支持128位的整形的加減乘除的操作。
用C++封裝一個數據結構的容量,比如hash table。
用C++封裝並實現一個智能指針(一定要使用模板)。
《設計模式》必需一讀,兩遍以上,思考一下,這23個模式的應用場景。主要是兩點:1)鍾愛組合而不是繼承,2)鍾愛介面而不是實現。(也推薦《深入淺出設計模式》)
實踐任務:
使用工廠模式實現一個內存池。
使用策略模式製做一個類其可以把文本文件進行左對齊,右對齊和中對齊。
使用命令模式實現一個命令行計算器,並支持undo和redo。
使用修飾模式實現一個酒店的房間價格訂價策略——旺季,服務,VIP、旅行團、等影響價格的因素。
學習STL的用法和其設計概念 - 容器,演算法,迭代器,函數子。如果可能,請讀一下其源碼。
實踐任務:嘗試使用面向對象、STL,設計模式、和WindowsSDK圖形編程的各種技能
做一個貪吃蛇或是俄羅斯方塊的游戲。支持不同的級別和難度。
做一個文件瀏覽器,可以瀏覽目錄下的文件,並可以對不同的文件有不同的操作,文本文件可以打開編輯,執行文件則執行之,mp3或avi文件可以播放,圖片文件可以展示圖片。
學習C++的一些類庫的設計,如: MFC(看看候捷老師的《深入淺出MFC》) ,Boost, ACE, CPPUnit,STL (STL可能會太難了,但是如果你能了解其中的設計模式和設計那就太好了,如果你能深入到我寫的《STL string類的寫時拷貝技術》那就非常不錯了,ACE需要很強在的系統知識,參見後面的「加強對系統的了解」)
Java是真正的面向對象的語言,Java的設計模式多得不能再多,也是用來學習面向對象的設計模式的最佳語言了(參看Java中的設計模式)。
推薦閱讀《Effective Java》 and 《Java解惑》
學習Java的框架,Java的框架也是多,如Spring, Hibernate,Struts 等等,主要是學習Java的設計,如IoC等。
Java的技術也是爛多,重點學習J2EE架構以及JMS, RMI, 等消息傳遞和遠程調用的技術。
學習使用Java做Web Service (官方教程在這里)
實踐任務: 嘗試在Spring或Hibernate框架下構建一個有網路的Web Service的遠程調用程序,並可以在兩個Service中通過JMS傳遞消息。
C++和Java都不是能在短時間內能學好的,C++玩是的深,Java玩的是廣,我建議兩者選一個。我個人的學習經歷是:
深究C++(我深究C/C++了十來年了)
學習Java的各種設計模式。
2、加強系統了解
重要閱讀下面的幾本書:
《Unix編程藝術》了解Unix系統領域中的設計和開發哲學、思想文化體系、原則與經驗。你一定會有一種醍醐灌頂的感覺。
《Unix網路編程卷1,套接字》這是一本看完你就明白網路編程的書。重要注意TCP、UDP,以及多路復用的系統調用select/poll/epoll的差別。
《TCP/IP詳解 卷1:協議》- 這是一本看完後你就可以當網路黑客的書。了解乙太網的的運作原理,了解TCP/IP的協議,運作原理以及如何TCP的調優。
實踐任務:
理解什麼是阻塞(同步IO),非阻塞(非同步IO),多路復用(select, poll, epoll)的IO技術。
寫一個網路聊天程序,有聊天伺服器和多個聊天客戶端(服務端用UDP對部分或所有的的聊天客戶端進Multicast或Broadcast)。
寫一個簡易的HTTP伺服器。
《Unix網路編程卷2,進程間通信》信號量,管道,共享內存,消息等各種IPC…… 這些技術好像有點老掉牙了,不過還是值得了解。
實踐任務:
主要實踐各種IPC進程序通信的方法。
嘗試寫一個管道程序,父子進程通過管道交換數據。
嘗試寫一個共享內存的程序,兩個進程通過共享內存交換一個C的結構體數組。
學習《Windows核心編程》一書。把CreateProcess,Windows線程、線程調度、線程同步(Event, 信號量,互斥量)、非同步I/O,內存管理,DLL,這幾大塊搞精通。
實踐任務:使用CreateProcess啟動一個記事本或IE,並監控該程序的運行。把前面寫過的那個簡易的HTTP服務用線程池實現一下。寫一個DLL的鉤子程序監控指定窗口的關閉事件,或是記錄某個窗口的按鍵。
有了多線程、多進程通信,TCP/IP,套接字,C++和設計模式的基本,你可以研究一下ACE了。使用ACE重寫上述的聊天程序和HTTP伺服器(帶線程池)
實踐任務:通過以上的所有知識,嘗試
寫一個服務端給客戶端傳大文件,要求把100M的帶寬用到80%以上。(注意,磁碟I/O和網路I/O可能會很有問題,想一想怎麼解決,另外,請注意網路傳輸最大單元MTU)
了解BT下載的工作原理,用多進程的方式模擬BT下載的原理。
3、系統架構
負載均衡。HASH式的,純動態式的。(可以到Google學術里搜一些關於負載均衡的文章讀讀)
多層分布式系統 – 客戶端服務結點層、計算結點層、數據cache層,數據層。J2EE是經典的多層結構。
CDN系統 – 就近訪問,內容邊緣化。
P2P式系統,研究一下BT和電驢的演算法。比如:DHT演算法。
伺服器備份,雙機備份系統(Live-Standby和Live-Live系統),兩台機器如何通過心跳監測對方?集群主結點備份。
虛擬化技術,使用這個技術,可以把操作系統當應用程序一下切換或重新配置和部署。
學習Thrift,二進制的高性能的通訊中間件,支持數據(對象)序列化和多種類型的RPC服務。
學習Hadoop。Hadoop框架中最核心的設計就是:MapRece和HDFS。MapRece的思想是由Google的一篇論文所提及而被廣為流傳的,簡單的一句話解釋MapRece就是「任務的分解與結果的匯總」。HDFS是Hadoop分布式文件系統(Hadoop Distributed File System)的縮寫,為分布式計算存儲提供了底層支持。
了解NoSQL資料庫(有人說可能是一個過渡炒作的技術),不過因為超大規模以及高並發的純動態型網站日漸成為主流,而SNS類網站在數據存取過程中有著實時性等剛性需求,這使得目前NoSQL資料庫慢慢成了人們所關注的焦點,並大有成為取代關系型資料庫而成為未來主流數據存儲模式的趨勢。當前NoSQL資料庫很多,大部分都是開源的,其中比較知名的有:MemcacheDB、Redis、Tokyo Cabinet(升級版為Kyoto Cabinet)、Flare、MongoDB、CouchDB、Cassandra、Voldemort等。

『伍』 代碼重構的概述

重構(),通過調整程序代碼改善軟體的質量、性能,使其程序的設計模式和架構更趨合理,提高軟體的擴展性和維護性。也許有人會問,為什麼不在項目開始時多花些時間把設計做好,而要以後花時間來重構呢?要知道一個完美得可以預見未來任何變化的設計,或一個靈活得可以容納任何擴展的設計是不存在的。系統設計人員對即將著手的項目往往只能從大方向予以把控,而無法知道每個細枝末節,其次永遠不變的就是變化,提出需求的用戶往往要在軟體成型後,才開始品頭論足,系統設計人員畢竟不是先知先覺的神仙,功能的變化導致設計的調整再所難免。所以測試為先,持續重構作為良好開發習慣被越來越多的人所採納,測試和重構像黃河的護堤,成為保證軟體質量的法寶。 在不改變系統功能的情況下,改變系統的實現方式。為什麼要這么做?投入精力不用來滿足客戶關心的需求,而是僅僅改變了軟體的實現方式,這是否是在浪費客戶的投資呢?
重構的重要性要從軟體的生命周期說起。軟體不同與普通的產品,他是一種智力產品,沒有具體的物理形態。一個軟體不可能發生物理損耗,界面上的按鈕永遠不會因為按動次數太多而發生接觸不良。那麼為什麼一個軟體製造出來以後,卻不能永遠使用下去呢?
對軟體的生命造成威脅的因素只有一個:需求的變更。一個軟體總是為解決某種特定的需求而產生,時代在發展,客戶的業務也在發生變化。有的需求相對穩定一些,有的需求變化的比較劇烈,還有的需求已經消失了,或者轉化成了別的需求。在這種情況下,軟體必須相應的改變。
考慮到成本和時間等因素,當然不是所有的需求變化都要在軟體系統中實現。但是總的說來,軟體要適應需求的變化,以保持自己的生命力。
這就產生了一種糟糕的現象:軟體產品最初製造出來,是經過精心的設計,具有良好架構的。但是隨著時間的發展、需求的變化,必須不斷的修改原有的功能、追加新的功能,還免不了有一些缺陷需要修改。為了實現變更,不可避免的要違反最初的設計構架。經過一段時間以後,軟體的架構就千瘡百孔了。bug越來越多,越來越難維護,新的需求越來越難實現,軟體的構架對新的需求漸漸的失去支持能力,而是成為一種制約。最後新需求的開發成本會超過開發一個新的軟體的成本,這就是這個軟體系統的生命走到盡頭的時候。
重構就能夠最大限度的避免這樣一種現象。系統發展到一定階段後,使用重構的方式,不改變系統的外部功能,只對內部的結構進行重新的整理。通過重構,不斷的調整系統的結構,使系統對於需求的變更始終具有較強的適應能力。
通過重構可以達到以下的目標:
·持續糾偏和改進軟體設計
重構和設計是相輔相成的,它和設計彼此互補。有了重構,你仍然必須做預先的設計,但是不必是最優的設計,只需要一個合理的解決方案就夠了,如果沒有重構、程序設計會逐漸腐敗變質,愈來愈像斷線的風箏,脫韁的野馬無法控制。重構其實就是整理代碼,讓所有帶著發散傾向的代碼回歸本位。
·
Martin Flower在《重構》中有一句經典的話:任何一個傻瓜都能寫出計算機可以理解的程序,只有寫出人類容易理解的程序才是優秀的程序員。對此,筆者感觸很深,有些程序員總是能夠快速編寫出可運行的代碼,但代碼中晦澀的命名使人暈眩得需要緊握坐椅扶手,試想一個新兵到來接手這樣的代碼他會不會想當逃兵呢?
軟體的生命周期往往需要多批程序員來維護,我們往往忽略了這些後來人。為了使代碼容易被他人理解,需要在實現軟體功能時做許多額外的事件,如清晰的排版布局,簡明扼要的注釋,其中命名也是一個重要的方面。一個很好的辦法就是採用暗喻命名,即以對象實現的功能的依據,用形象化或擬人化的手法進行命名,一個很好的態度就是將每個代碼元素像新生兒一樣命名,也許筆者有點命名偏執狂的傾向,如能榮此雅號,將深以此為幸。
對於那些讓人充滿迷茫感甚至誤導性的命名,需要果決地、大刀闊斧地整容,永遠不要手下留情!
·幫助發現隱藏的代碼缺陷
孔子說過:溫故而知新。重構代碼時逼迫你加深理解原先所寫的代碼。筆者常有寫下程序後,卻發生對自己的程序邏輯不甚理解的情景,曾為此驚悚過,後來發現這種症狀居然是許多程序員常患的感冒。當你也發生這樣的情形時,通過重構代碼可以加深對原設計的理解,發現其中的問題和隱患,構建出更好的代碼。
·從長遠來看,有助於提高編程效率
當你發現解決一個問題變得異常復雜時,往往不是問題本身造成的,而是你用錯了方法,拙劣的設計往往導致臃腫的編碼。
改善設計、提高可讀性、減少缺陷都是為了穩住陣腳。良好的設計是成功的一半,停下來通過重構改進設計,或許會在當前減緩速度,但它帶來的後發優勢卻是不可低估的。 新官上任三把火,開始一個全新??、腳不停蹄、加班加點,一支聲勢浩大的千軍萬碼夾裹著程序員激情和扣擊鍵盤的鳴金奮力前行,勢如破竹,攻城掠地,直指黃龍府。
開發經理是這支浩浩湯湯代碼隊伍的統帥,他負責這支隊伍的命運,當齊桓公站在山頂上看到管仲訓練的隊伍整齊劃一地前進時,他感嘆說我有這樣一支軍隊哪裡還怕沒有勝利呢?。但很遺憾,你手中的這支隊伍原本只是散兵游勇,在前進中招兵買馬,不斷壯大,所以隊伍變形在所難免。當開發經理發覺隊伍變形時,也許就是克制住攻克前方山頭的誘惑,停下腳步整頓隊伍的時候了。
Kent Beck提出了代碼壞味道的說法,和我們所提出的隊伍變形是同樣的意思,隊伍變形的信號是什麼呢?以下列述的代碼症狀就是隊伍變形的強烈信號:
·代碼中存在重復的代碼
中國有118 家整車生產企業,數量幾乎等於美、日、歐所有汽車廠家數之和,但是全國的年產量卻不及一個外國大汽車公司的產量。重復建設只會導致效率的低效和資源的浪費。
程序代碼更是不能搞重復建設,如果同一個類中有相同的代碼塊,請把它提煉成類的一個獨立方法,如果不同類中具有相同的代碼,請把它提煉成一個新類,永遠不要重復代碼。
·過大的類和過長的方法
過大的類往往是類抽象不合理的結果,類抽象不合理將降低了代碼的復用率。方法是類王國中的諸侯國,諸侯國太大勢必動搖中央集權。過長的方法由於包含的邏輯過於復雜,錯誤機率將直線上升,而可讀性則直線下降,類的健壯性很容易被打破。當看到一個過長的方法時,需要想辦法將其劃分為多個小方法,以便於分而治之。
·牽一毛而需要動全身的修改
當你發現修改一個小功能,或增加一個小功能時,就引發一次代碼地震,也許是你的設計抽象度不夠理想,功能代碼太過分散所引起的。
·類之間需要過多的通訊
A類需要調用B類的過多方法訪問B的內部數據,在關繫上這兩個類顯得有點狎昵,可能這兩個類本應該在一起,而不應該分家。
·過度耦合的信息鏈
計算機是這樣一門科學,它相信可以通過添加一個中間層解決任何問題,所以往往中間層會被過多地追加到程序中。如果你在代碼中看到需要獲取一個信息,需要一個類的方法調用另一個類的方法,層層掛接,就象輸油管一樣節節相連。這往往是因為銜接層太多造成的,需要查看就否有可移除的中間層,或是否可以提供更直接的調用方法。
·各立山頭幹革命
如果你發現有兩個類或兩個方法雖然命名不同但卻擁有相似或相同的功能,你會發現往往是因為開發團隊協調不夠造成的。筆者曾經寫了一個頗好用的字元串處理類,但因為沒有及時通告團隊其他人員,後來發現項目中居然有三個字元串處理類。革命資源是珍貴的,我們不應各立山頭幹革命。
·不完美的設計
在筆者剛完成的一個比對報警項目中,曾安排阿朱開發報警模塊,即通過Socket向指定的簡訊平台、語音平台及客戶端報警器插件發送報警報文信息,阿朱出色地完成了這項任務。後來用戶又提出了實時比對的需求,即要求第三方系統以報文形式向比對報警系統發送請求,比對報警系統接收並響應這個請求。這又需要用到Socket報文通訊,由於原來的設計沒有將報文通訊模塊獨立出來,所以無法復用阿朱開發的代碼。後來我及時調整了這個設計,新增了一個報文收發模塊,使系統所有的對外通訊都復用這個模塊,系統的整體設計也顯得更加合理。
每個系統都或多或少存在不完美的設計,剛開始可能注意不到,到後來才會慢慢凸顯出來,此時唯有勇於更改才是最好的出路。
·缺少必要的注釋
雖然許多軟體工程的書籍常提醒程序員需要防止過多注釋,但這個擔心好象並沒有什麼必要。往往程序員更感興趣的是功能實現而非代碼注釋,因為前者更能帶來成就感,所以代碼注釋往往不是過多而是過少,過於簡單。人的記憶曲線下降的坡度是陡得嚇人的,當過了一段時間後再回頭補注釋時,很容易發生提筆忘字,愈言且止的情形。
曾在網上看到過微軟的代碼注釋,其詳盡程度讓人嘆為觀止,也從中體悟到了微軟成功的一個經驗。 學習一種可以大幅提高生產力的新技術時,你總是難以察覺其不適用的場合。通常你在一個特定場景中學習它,這個場景往往是個項目。這種情況下你很難看出什麼會造成這種新技術成效不彰或甚至形成危害。十年前,對象技術(object tech.)的情況也是如此。那時如果有人問我「何時不要使用對象」,我很難回答。並非我認為對象十全十美、沒有局限性 — 我最反對這種盲目態度,而是盡管我知道它的好處,但確實不知道其局限性在哪兒。
現在,重構的處境也是如此。我們知道重構的好處,我們知道重構可以給我們的工作帶來垂手可得的改變。但是我們還沒有獲得足夠的經驗,我們還看不到它的局限性。
這一小節比我希望的要短。暫且如此吧。隨著更多人學會重構技巧,我們也將對??你應該嘗試一下重構,獲得它所提供的利益,但在此同時,你也應該時時監控其過程,注意尋找重構可能引入的問題。請讓我們知道你所遭遇的問題。隨著對重構的了解日益增多,我們將找出更多解決辦法,並清楚知道哪些問題是真正難以解決的。
·資料庫(Databases)
「重構」經常出問題的一個領域就是資料庫。絕大多數商用程序都與它們背後的database schema(資料庫表格結構)緊密耦合(coupled)在一起,這也是database schema如此難以修改的原因之一。另一個原因是數據遷移(migration)。就算你非常小心地將系統分層(layered),將database schema和對象模型(object model)間的依賴降至最低,但database schema的改變還是讓你不得不遷移所有數據,這可能是件漫長而煩瑣的工作。
在「非對象資料庫」(nonobject databases)中,解決這個問題的辦法之一就是:在對象模型(object model)和資料庫模型(database model)之間插入一個分隔層(separate layer),這就可以隔離兩個模型各自的變化。升級某一模型時無需同時升級另一模型,只需升級上述的分隔層即可。這樣的分隔層會增加系統復雜度,但可以給你很大的靈活度。如果你同時擁有多個資料庫,或如果資料庫模型較為復雜使你難以控制,那麼即使不進行重構,這分隔層也是很重要的。
你無需一開始就插入分隔層,可以在發現對象模型變得不穩定時再產生它。這樣你就可以為你的改變找到最好的杠桿效應。
對開發者而言,對象資料庫既有幫助也有妨礙。某些面向對象資料庫提供不同版本的對象之間的自動遷移功能,這減少了數據遷移時的工作量,但還是會損失一定時間。如果各資料庫之間的數據遷移並非自動進行,你就必須自行完成遷移工作,這個工作量可是很大的。這種情況下你必須更加留神classes內的數據結構變化。你仍然可以放心將classes的行為轉移過去,但轉移值域(field)時就必須格外小心。數據尚未被轉移前你就得先運用訪問函數(accessors)造成「數據已經轉移」的假象。一旦你確定知道「數據應該在何處」時,就可以一次性地將數據遷移過去。這時惟一需要修改的只有訪問函數(accessors),這也降低了錯誤風險。
·修改介面(Changing Interfaces)
關於對象,另一件重要事情是:它們允許你分開修改軟體模塊的實現(implementation)和介面(interface)。你可以安全地修改某對象內部而不影響他人,但對於介面要特別謹慎 — 如果介面被修改了,任何事情都有可能發生。
一直對重構帶來困擾的一件事就是:許多重構手法的確會修改介面。像Rename Method(273)這么簡單的重構手法所做的一切就是修改介面。這對極為珍貴的封裝概念會帶來什麼影響呢?
如果某個函數的所有調用動作都在你的控制之下,那麼即使修改函數名稱也不會有任何問題。哪怕面對一個public函數,只要能取得並修改其所有調用者,你也可以安心地將這個函數易名。只有當需要修改的介面系被那些「找不到,即使找到也不能修改」的代碼使用時,介面的修改才會成為問題。如果情況真是如此,我就會說:這個介面是個「已發布介面」(published interface)— 比公開介面(public interface)更進一步。介面一旦發行,你就再也無法僅僅修改調用者而能夠安全地修改介面了。你需要一個略為復雜的程序。
這個想法改變了我們的問題。如今的問題是:該如何面對那些必須修改「已發布介面」的重構手法?
簡言之,如果重構手法改變了已發布介面(published interface),你必須同時維護新舊兩個介面,直到你的所有用戶都有時間對這個變化做出反應。幸運的是這不太困難。你通常都有辦法把事情組織好,讓舊介面繼續工作。請盡量這么做:讓舊介面調用新介面。當你要修改某個函數名稱時,請留下舊函數,讓它調用新函數。千萬不要拷貝函數實現碼,那會讓你陷入「重復代碼」(plicated code)的泥淖中難以自拔。你還應該使用Java提供的 deprecation(反對)設施,將舊介面標記為 deprecated。這么一來你的調用者就會注意到它了。
這個過程的一個好例子就是Java容器類(collection classes)。Java 2的新容器取代了原先一些容器。當Java 2容器發布時,JavaSoft花了很大力氣來為開發者提供一條順利遷徙之路。
「保留舊介面」的辦法通常可行,但很煩人。起碼在一段時間里你必須建造(build)並維護一些額外的函數。它們會使介面變得復雜,使介面難以使用。還好我們有另一個選擇:不要發布(publish)介面。當然我不是說要完全禁止,因為很明顯你必得發布一些介面。如果你正在建造供外部使用的APIs,像Sun所做的那樣,肯定你必得發布介面。我之所以說盡量不要發布,是因為我常常看到一些開發團隊公開了太多介面。我曾經看到一支三人團隊這么工作:每個人都向另外兩人公開發布介面。這使他們不得不經常來回維護介面,而其實他們原本可以直接進入程序庫,徑行修改自己管理的那一部分,那會輕松許多。過度強調「代碼擁有權」的團隊常常會犯這種錯誤。發布介面很有用,但也有代價。所以除非真有必要,別發布介面。這可能意味需要改變你的代碼擁有權觀念,讓每個人都可以修改別人的代碼,以運應介面的改動。以搭檔(成對)編程(Pair Programming)完成這一切通常是個好主意。
不要過早發布(published)介面。請修改你的代碼擁有權政策,使重構更順暢。
Java之中還有一個特別關於「修改介面」的問題:在throws子句中增加一個異常。這並不是對簽名式(signature)的修改,所以你無法以delegation(委託手法)隱藏它。但如果用戶代碼不作出相應修改,編譯器不會讓它通過。這個問題很難解決。你可以為這個函數選擇一個新名tion(可控式異常)轉換成一個unchecked exception(不可控異常)。你也可以拋出一個unchecked異常,不過這樣你就會失去檢驗能力。如果你那麼做,你可以警告調用者:這個unchecked異常日後會變成一個checked異常。這樣他們就有時間在自己的代碼中加上對此異常的處理。出於這個原因,我總是喜歡為整個package定義一個superclass異常(就像java.sql的SQLException),並確保所有public函數只在自己的throws子句中聲明這個異常。這樣我就可以隨心所欲地定義subclass異常,不會影響調用者,因為調用者永遠只知道那個更具一般性的superclass異常。
·難以通過重構手法完成的設計改動
通過重構,可以排除所有設計錯誤嗎?是否存在某些核心設計決策,無法以重構手法修改?在這個領域里,我們的統計數據尚不完整。當然某些情況下我們可以很有效地重構,這常常令我們倍感驚訝,但的確也有難以重構的地方。比如說在一個項目中,我們很難(但還是有可能)將「無安全需求(no security requirements)情況下構造起來的系統」重構為「安全性良好的(good security)系統」。
這種情況下我的辦法就是「先想像重構的情況」。考慮候選設計方案時,我會問自己:將某個設計重構為另一個設計的難度有多大?如果看上去很簡單,我就不必太擔心選擇是否得當,於是我就會選最簡單的設計,哪怕它不能覆蓋所有潛在需求也沒關系。但如果預先看不到簡單的重構辦法,我就會在設計上投入更多力氣。不過我發現,這種情況很少出現。
·何時不該重構?
有時候你根本不應該重構 — 例如當你應該重新編寫所有代碼的時候。有時候既有代碼實在太混亂,重構它還不如從新寫一個來得簡單。作出這種決定很困難,我承認我也沒有什麼好准則可以判斷何時應該放棄重構。
重寫(而非重構)的一個清楚訊號就是:現有代碼根本不能正常運作。你可能只是試著做點測試,然後就發現代碼中滿是錯誤,根本無法穩定運作。記住,重構之前,代碼必須起碼能夠在大部分情況下正常運作。
一個折衷辦法就是:將「大塊頭軟體」重構為「封裝良好的小型組件」。然後你就可以逐一對組件作出「重構或重建」的決定。這是一個頗具希望的辦法,但我還沒有足夠數據,所以也無法寫出優秀的指導原則。對於一個重要的古老系統,這肯定會是一個很好的方向。
另外,如果項目已近最後期限,你也應該避免重構。在此時機,從重構過程贏得的生產力只有在最後期限過後才能體現出來,而那個時候已經時不我予。Ward Cunningham對此有一個很好的看法。他把未完成的重構工作形容為「債務」。很多公司都需要借債來使自己更有效地運轉。但是借債就得付利息,過於復雜的代碼所造成的「維護和擴展的額外開銷」就是利息。你可以承受一定程度的利息,但如果利息太高你就會被壓垮。把債務管理好是很重要的,你應該隨時通過重構來償還一部分債務。
如果項目已經非常接近最後期限,你不應該再分心於重構,因為已經沒有時間了。不過多個項目經驗顯示:重構的確能夠提高生產力。如果最後你沒有足夠時間,通常就表示你其實早該進行重構。 「重構」肩負一項特別任務:它和設計彼此互補。初學編程的時候,我埋頭就寫程序,渾渾噩噩地進行開發。然而很快我便發現,「事先設計」(upfront design)可以助我節省回頭工的高昂成本。於是我很快加強這種「預先設計」風格。許多人都把設計看作軟體開發的關鍵環節,而把編程(programming)看作只是機械式的低級勞動。他們認為設計就像畫工程圖而編碼就像施工。但是你要知道,軟體和真實器械有著很大的差異。軟體的可塑性更強,而且完全是思想產品。正如Alistair Cockburn所說:『有了設計,我可以思考更快,但是其中充滿小漏洞。』
有一種觀點認為:重構可以成為「預先設計」的替代品。這意思是你根本不必做任何設計,只管按照最初想法開始編碼,讓代碼有效運作,然後再將它重構成型。事實上這種辦法真的可行。我的確看過有人這么做,最後獲得設計良好的軟體。極限編程(Extreme Programming)【Beck, XP】 的支持者極力提倡這種辦法。
盡管如上所言,只運用重構也能收到效果,但這並不是最有效的途徑。是的,即使極限編程(Extreme Programming)愛好者也會進行預先設計。他們會使用CRC卡或類似的東西來檢驗各種不同想法,然後才得到第一個可被接受的解決方案,然後才能開始編碼,然後才能重構。關鍵在於:重構改變了「預先設計」的角色。如果沒有重構,你就必須保證「預先設計」正確無誤,這個壓力太大了。這意味如果將來需要對原始設計做任何修改,代價都將非常高昂。因此你需要把更多時間和精力放在預先設計上,以避免日後修改。
如果你選擇重構,問題的重點就轉變了。你仍然做預先設計,但是不必一定找出正確的解決方案。此刻的你只需要得到一個足夠合理的解決方案就夠了。你很肯定地知道,在實現這個初始解決方案的時候,你對問題的理解也會逐漸加深,你可能會察覺最佳解決方案和你當初設想的有些不同。只要有重構這項武器在手,就不成問題,因為重構讓日後的修改成本不再高昂。
這種轉變導致一個重要結果:軟體設計朝向簡化前進了一大步。過去未曾運用重構時,我總是力求得到靈活的解決方案。任何一個需求都讓我提心吊膽地猜疑:在系統壽命期間,這個需求會導致怎樣的變化?由於變更設計的代價非常高昂,所以我希望建造一個足夠靈活、足夠強固的解決方案,希望它能承受我所能預見的所有需求變化。問題在於:要建造一個靈活的解決方案,所需的成本難以估算。靈活的解決方案比簡單的解決方案復雜許多,所以最終得到的軟體通常也會更難維護 — 雖然它在我預先設想的??方向上,你也必須理解如何修改設計。如果變化只出現在一兩個地方,那不算大問題。然而變化其實可能出現在系統各處。如果在所有可能的變化出現地點都建立起靈活性,整個系統的復雜度和維護難度都會大大提高。當然,如果最後發現所有這些靈活性都毫無必要,這才是最大的失敗。你知道,這其中肯定有些靈活性的確派不上用場,但你卻無法預測到底是哪些派不上用場。為了獲得自己想要的靈活性,你不得不加入比實際需要更多的靈活性。
有了重構,你就可以通過一條不同的途徑來應付變化帶來的風險。你仍舊需要思考潛在的變化,仍舊需要考慮靈活的解決方案。但是你不必再逐一實現這些解決方案,而是應該問問自己:『把一個簡單的解決方案重構成這個靈活的方案有多大難度?』如果答案是「相當容易」(大多數時候都如此),那麼你就只需實現目前的簡單方案就行了。
重構可以帶來更簡單的設計,同時又不損失靈活性,這也降低了設計過程的難度,減輕了設計壓力。一旦對重構帶來的簡單性有更多感受,你甚至可以不必再預先思考前述所謂的靈活方案 — 一旦需要它,你總有足夠的信心去重構。是的,當下只管建造可運行的最簡化系統,至於靈活而復雜的設計,唔,多數時候你都不會需要它。
勞而無獲— Ron Jeffries
Chrysler Comprehensive Compensation(克萊斯勒綜合薪資系統)的支付過程太慢了。雖然我們的開發還沒結束,這個問題卻已經開始困擾我們,因為它已經拖累了測試速度。
Kent Beck、Martin Fowler和我決定解決這個問題。等待大夥兒會合的時間里,憑著我對這個系統的全盤了解,我開始推測:到底是什麼讓系統變慢了?我想到數種可能,然後和夥伴們談了幾種可能的修改方案。最後,關於「如何讓這個系統運行更快」,我們提出了一些真正的好點子。
然後,我們拿Kent的量測工具度量了系統性能。我一開始所想的可能性竟然全都不是問題肇因。我們發現:系統把一半時間用來創建「日期」實體(instance)。更有趣的是,所有這些實體都有相同的值。
於是我們觀察日期的創建邏輯,發現有機會將它優化。日期原本是由字元串轉換而生,即使無外部輸入也是如此。之所以使用字元串轉換方式,完全是為了方便鍵盤輸入。好,也許我們可以將它優化。
於是我們觀察日期怎樣被這個程序運用。我們發現,很多日期對象都被用來產生「日期區間」實體(instance)。「日期區間」是個對象,由一個起始日期和一個結束日期組成。仔細追蹤下去,我們發現絕大多數日期區間是空的!
處理日期區間時我們遵循這樣一個規則:如果結束日期在起始日期之前,這個日期區間就該是空的。這是一條很好的規則,完全符合這個class的需要。採用此一規則後不久,我們意識到,創建一個「起始日期在結束日期之後」的日期區間,仍然不算是清晰的代碼,於是我們把這個行為提煉到一個factory method(譯註:一個著名的設計模式,見《Design Patterns》),由它專門創建「空的日期區間」。
我們做了上述修改,使代碼更加清晰,卻意外得到了一個驚喜。我們創建一個固定不變的「空日期區間」對象,並讓上述調整後的factory method每次都返回該對象,而不再每次都創建新對象。這一修改把系統速度提升了幾乎一倍,足以讓測試速度達到可接受程度。這只花了我們大約五分鍾。
我和團隊成員(Kent和Martin謝絕參加)認真推測過:我們瞭若指掌的這個程序中可能有什麼錯誤?我們甚至憑空做了些改進設計,卻沒有先對系統的真實情況進行量測。
我們完全錯了。除了一場很有趣的交談,我們什麼好事都沒做。
教訓:哪怕你完全了解系統,也請實際量測它的性能,不要臆測。臆測會讓你學到一些東西,但十有八九你是錯的。

閱讀全文

與程序員重構水管圖相關的資料

熱點內容
安卓天氣掛件怎麼放到第一頁 瀏覽:865
蘭州理工大學51單片機實驗 瀏覽:35
程序員需要高深嗎 瀏覽:98
農行app怎麼查詢公戶賬戶余額 瀏覽:330
美國疾控發防疫命令 瀏覽:141
用固定循環編程可以 瀏覽:880
硅膠壓縮比測試 瀏覽:801
vc命令行編譯c 瀏覽:674
php用戶登錄界面 瀏覽:82
安卓車載導航如何卸載自帶軟體 瀏覽:714
階乘的編程c 瀏覽:415
java視頻教程達內 瀏覽:825
單片機應該怎麼學 瀏覽:420
空氣壓縮機品牌名稱 瀏覽:346
word文檔部分內容加密 瀏覽:63
壓解壓軟體 瀏覽:936
java設置excel格式 瀏覽:957
單片機鎖存器地址怎麼看 瀏覽:576
手機硬體編程 瀏覽:835
如何去除你看文件夾時間 瀏覽:442