<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    零雨其蒙's Blog

    做優秀的程序員
    隨筆 - 59, 文章 - 13, 評論 - 58, 引用 - 0
    數據加載中……

    假期學習筆記(二)

    2 、重構

    ???? 重構的概念不是源自 Martin Fowler 這部名著《重構——改善既有代碼的設計》(《 Refactoring Improving the Design of Existing Code 》),而是來自 Smalltalk 社區,感謝 Fowler 此書是用我最熟悉的 Java 來作為所舉實例的語言的,這使我閱讀此書比較省勁,而不像閱讀《設計模式》,畢竟對 C++ 沒那么多經驗。這本書一共 15 章,我看到第 6 章。第 1 章重構,第一個案例,用 Step by Step 的方式完成了一系列重構;第 2 章:重構原則,介紹了什么是重構和一些與之相關的話題,大都是經驗之談,沒什么代碼;第 3 章讓我思考了很多,它主要介紹了代碼的壞味道,一共 22 Bad Smell 。其中大部分都是我在實際編程過程中遇到過或認真思考過的,因此就有一種遇到老朋友的欣喜,看得格外起勁。而后第 4 章主要討論了構筑測試體系,這部分內容也是讓我深有感觸的。第 5 章是本書所講的主體真正開始的序幕,介紹了一份統領全書的重構名錄,起到了提綱挈領的作用;然后第 6 章介紹了第一類重構方法:重新組織你的函數( Compsosing Methods ),很多方法正是我平常使用的——使用時并不知道這樣做就是重構,也不知道有這些名稱——不過,這些方法都來自于實踐,因此會產生這些共鳴也是理所當然的。但是一些壞味道我也不完全理解,在編程時沒有遇到過,有些重構手法在我看來也并沒有太大必要,這些可能有待日后編程經驗的進一步增加才能更好的理解吧。

    2.1 代碼的壞味道

    ??? 用味道來形容代碼質量,是一種很屌也很流行的說法,原來的一些叫法都比較土,總之先前也看過很多被稱為好的編程風格的文章,比如討論一個函數到底最長有多長才算合理之類的。這本書里也包含這些內容,不過比我以前看過的要多。下面只挑一些我遇到過的,并且有些話要說的 Bad Smell 來討論之。

    2.1.1 Duplicated Code (重復代碼)

    重復代碼被 Fowler 作為第一種壞味道提出來,或許因為它是目前最常見的問題,同時也是這個人人宣傳要“代碼復用”“最大程度的重用”的時代的主旋律所不許可的。重復代碼在實際的項目中非常害人,主要有以下幾個缺點:

    u?????? 同樣的代碼,一旦修改,需要多處維護,非常麻煩,還容易出錯。在做那個人力資源系統時我就飽受此苦,修改別人的代碼,不知道那是哪位仁兄編的,同樣的代碼出現了好幾次,搞得我改了這段改那段,相當麻煩。還有前一陣在地磅系統中加入視頻監控功能,在那個擁有 6 if 語句的函數里,每個分支語句下都在干一件事,向數據庫的 4 個表中插入數據, 不同的是插入的數據略有區別,但表是一樣的。我需要把我寫的函數加入到這六個分支語句里,而且必須要找準插入點,每個分支語句下的代碼都有五、六十行,當時就在想,這家伙當初編程時怎么不把插入數據庫的代碼寫成一個函數呢?(當然用 DAO 更好了)這些項目都是用 Delphi 完成的,而且整個代碼量奇大無比,領域知識及其復雜,先前的程序員還不按套路出牌,想把重復的代碼重構成一個函數( Extract Method ,提煉函數)都不敢,因為不知道會產生什么可怕的后果。美國有句土話:如果沒壞就接著用。不過,這樣的隱患隨著代碼量的持續增加,會越來越難纏——中國的很多所謂架構師都不重視代碼質量,殊不知最后我們交付的最有價值的是可以工作的軟件,如果陷入壞味道代碼的泥潭,最后想脫身都難,很容易導致項目失敗(關于這個話題,在《代碼大全》里有很精辟的闡述)。

    u?????? 編寫重復的代碼會浪費時間,降低生產力。有人可能覺得沒什么,因為重復的代碼都是復制粘貼產生的,但是在實際的開發中,我們大都有這樣的經歷(特別是做沒什么太高難的技術含量的企業信息系統的開發),往往編寫新的功能只需要幾分鐘(但是功能不能太大,反正我是這樣,當設計做好了之后,基本上都是在生產代碼了。特別是熟練之后,不需要查手冊幫助之類的,真的很快),而大量的時間都是在調試了,編寫重復的代碼,意味著每一段重復的代碼在添加同樣的新代碼后都需要調試——很奇怪,這常常會出錯,特別是工作環境很惡劣的時候。

    2.1.2 Long Method (過長函數)

    ???? 剛才我所說的那個包含 6 if 語句的函數有 10 來屏(使用 Delphi 7 17 寸純平顯示器),真是痛苦的不行,往往看了后面忘了前面,估計當時寫這個函數的人可能很爽,想到什么寫什么,這可苦了我們后面這些人,理解這個函數花了我 6 個小時——這豈不是在大大降低生產率嗎?以前看李開復寫過的一篇文章,說微軟以前有個大牛寫了個 1000 多行的函數,而且這個函數還難度極高,沒有一句重復一句廢話,并且改動一句整個系統就不能用了,搞得后來沒人敢改這個函數。好像后來有人把這個函數重寫了,分成了很多小函數。

    ???? 一個函數多長算長?很多人說最長不要超過一屏,我覺得這個標準是個可視化的標準,可以量化,不過有些死板。我覺得這要視實際情況而定,函數不能太長,同時也不能過于瑣碎,分得塊太小也不太便于理解,我想還是根據領域模型來考慮函數長度吧,每個函數只干自己的事,不要越俎代庖,反正從我的經驗來看,按照這個原則就不會編寫出特別長的函數,特別是使用 Java 這種優雅的面向對象語言。一件事情有 30 個步驟怎么也做完了吧,加上那些聲明、創建的代碼,五六十行應該都能搞定了。五六十行超過一屏了,不過也不會太糟糕,因為核心算法的代碼都在一屏里,不會有什么太大的影響。

    ?

    2.1.3 Large Class (過大類)

    其實與 Long Method 一樣,一個類封裝了太多的方法,但是我遇到這種情況都是發生在沒有正確運用 OO ,甚至根本不知道 OO 的時候,這很有問題,雖然很多人使用 Java 編程,但是并不意味他們是運用 OO 的思想在進行程序設計,我經常看到這樣的 Javabean ,里面包含了一個模塊所有方法——我通常把它叫做 Method Container ,認真學習 OO 設計,應該不會出現如此龐大的類(包含在 class 中的未必是類,類要有意義,否則就是函數集,不能稱為類)。

    2.1.4 Long Parameter List (過長參數列)

    讓我印象最深刻的一次遇到這個問題是做視頻監控系統時,起初只是為構造函數添加參數,結果參數越來越多,竟然有 7 個,后來發現很多參數其實是一個類的屬性,當時很后悔起初要是按照 OO 設計就不會出現這樣的情況了,最初圖省事,結果后來反倒麻煩,不得不重構,采用 Preserve Whole Object 288 )搞定了。

    ?

    2.1.5 Switch Statements switch 驚悚現身)

    前面提到的那個視頻系統出現 6 if 語句,其實這 6 if 語句是包含在 switch 分支中的一個 case 里的。這個 switch 比那 6 if 語句還要恐怖,它不僅意味著重復代碼,因為兩個分支在執行類似的代碼,只是變量賦給的值不同,還意味著魔法數字的出現。由分支條件控制,整個程序在這兩段中來回蹦(謝天謝地只有兩個分支)。

    ?

    2.1.6 Lazy Class (冗贅類)

    ? 有些因為經過不斷重構而逐步失去自身價值的類——只剩下一個空殼了——就讓它消失吧。這種類在項目中實在是讓后續的開發人員浪費大量寶貴時間。其實這一點推而廣之就是程序中任何沒什么用處的代碼立馬刪掉,然而現實的某些想法常常讓人放棄這么做,最可怕的是在最開始這種現象初露端倪時姑息縱容,到后來就變得愈發不可收拾了。這種現實想法往往是舊的代碼先不要刪,這樣如果增加的代碼不能更好的工作可以退回去,然而新的代碼可以正常工作后,又懶得去管那些廢棄的代碼了,于是就留下了很多遺留代碼。由于大型項目往往都不會是一伙人做完,跨越六七年的項目中途換將也是有情可原,即使不會換,但是維護和開發也有可能是兩撥人,這樣后繼者也不知道哪些代碼是有用的哪些是廢棄的,很多時間浪費在了讀根本沒有價值的廢代碼上了,而且大家還不敢刪除,怕引起麻煩,這樣廢代碼越來越多,最后有價值的代碼被廢代碼淹沒了,項目也就死了。

    2.1.7 Speculative Generality (夸夸其談未來性)

    這種現象也特別普遍,很多人都在為莫須有的功能努力,記得網上流傳的真正的程序員的特點有一條叫真正的程序員認為自己比用戶更清楚用戶想要什么。在某些情況下確實是這樣,因為畢竟我們可能做過許多類的項目,發現了客戶都是在軟件不斷的迭代中學習逐步了解自己需要什么,我們就可能產生某種預見性,估計到客戶也可能像過去那些同類客戶一樣,在未來產生這樣的需求。不過這個事情就不好說了,因為我也碰到過我們替用戶想需求,結果并不是客戶想要的,我們費了好大勁實現的功能客戶一點都不看好,只好廢棄重寫。我們那時候做人力資源系統中的工資模板管理模塊時就遇到了這種情況。

    2.1.8 Data Class (純稚的數據類)

    ? 這個就是被稱為啞對象的類, Rod Johnson 也曾在其著作中批評過 TO 這樣的啞對象。這種類只有屬性和存取器方法,沒有任何其他的方法。我對 Data Class 沒有那么極端的鮮明的好惡,因為那時候在用 JSP+Servlet+Javabean 模仿 MVC 框架和 O/R Mapping 框架時使用 VO PO 這樣的 Data Class ,覺得還是挺有作用的,它可以使得顯示層更加的單純,將邏輯封裝在業務門面中,然后將顯示結果封裝在 VO 里,然后直接與 JSP 頁面形成對應關系;數據訪問也同理,感覺很好的解決了一些問題。后來看 Struts Hibernate 時,發現它們借助 IDE 或其他工具把這種 Data Class 都自動生成了,覺得挺好的,因為那些類實在沒什么必要一個一個編寫。

    2.1.9 Comments (過多的注釋)

    Fowler 指正注釋過多是一種壞味道主要源于現實——糟糕的代碼試圖用長長的注釋來彌補,是不對的。然而在我遇到的情況往往是注釋亂寫,有的沒必要注釋的地方寫了很多注釋,反而很難理解的算法、流程反而沒有注釋了。有很多方法如 getUserByID int id ),本來很明確了,非要寫注釋;而一套復雜的商業規則,竟然一行注釋都沒有,僅有的注釋就是變量名(如果變量名如果起的能自名義,根本就不需要注釋)。這種現象的出現主要是作者本身也沒有特別清晰的思路,并沒有理順好算法或規則,所以寫不出清晰的注釋——但這種注釋很重要,要不鬼都看不懂,特別是出現魔法數字時(當然盡量避免出現這種情況)。

    2.1.10 其他

    一共有 22 種壞味道在這本書里,但是我只舉出 9 個,因為這 9 個屬于比較淺層次的,還有一些關于父類子類繼承關系的壞味道,因為我沒怎么用過這種繼承,周圍人也很幾乎沒用過,所以就沒遇過這種壞味道了。上面寫過的壞味道其實只是借助它們的名字說一些問題,這些問題造成了程序質量的低下,難以維護,難以擴展,容易出錯,看也看不懂,改也很費事。我想一個有經驗的程序員看了這 22 種壞味道(當然在實際項目開發中還有很多壞味道書里沒有提及),應該就會有自己的一些解決方案了,很多方法跟 Fowler 提供的方法是一樣的,這一點 Fowler 在書的前面也提到了。但是建議大家看看這本書, Fowler 已經提出了每種壞味道的解決方案,如果已經看完了所有的重構方法,也可以看這部分來復習所學的主要的重構方法。

    2.2 什么是重構

    ??????? 按照 Fowler 的說法,重構有兩個定義,一個是其作為名詞的定義,一個是其作為動詞的定義。

    重構(名詞):對軟件內部結構的一種調整,目的是在不改變 [ 軟件之可察行為 ] 前提下,提高其可理解性,降低其修改成本。

    重構(動詞):使用一系列重構準則(手法),在不改變 [ 軟件之可察行為 ] 前提下,調整其結構。

    相關的細節我就不解釋了,因為很簡單。

    2.3 構筑測試體系

    ??? 這一點也是我這次去廣州做項目體會到的,起初也沒寫測試程序,重構時擔驚受怕的,后來針對一些方法寫了測試程序,確實大大提高了效率,以后再作開發一定要寫測試,雖然表面上費時,但實際上可以省去很多反復調試的時間,那次是用 Delphi 7 ,也沒什么單元測試工具,自己寫測試比較麻煩,而在 Java 中有了 JUnit ,在 Delphi 2005 中也有了 DUnit ,應該好好珍惜,測試驅動是絕對有好處的。而且重構不寫測試,很容易改錯,我就是在修改程序時有一個對象原先需要創建,后來不需要了,我只刪掉了它的對象銷毀方法,卻忘了刪掉創建代碼,結果花了一天時間來找錯,后來實在不行了,編寫測試,才搞定了,天大的教訓啊。

    2.4 重新組織你的函數

    在這一部分我覺得最有價值的是 Extract Method (提煉函數),其他的倒是沒太發現太大的用處,可能是因為我沒遇到過這種情況吧。后面的有些方法我覺得有點過于純化代碼了,應該適度采取,在復雜的環境下,不排除它們是好的方法。(畢竟 Fowler 比我的經驗多得多,見過的項目和程序也多得多,以后可能會在某個時候用到它們)

    2.5 其他

    像《重構》這樣的書,和《設計模式》一樣,都不是看了一遍就可以束之高閣的,在實踐中遇到困難回過頭來看,慢慢才會真正提高。

    另外這本書本來熊節翻譯就好了嘛,我看他參與翻譯的《 J2EE 核心模式》不是很好嘛,干嘛非要拉上侯捷,最煩看他翻譯的書,不使用大陸通用的術語翻譯方法,還總是用些不明不白含糊其辭的文言,看了真是不爽。我還買了本英文版的,等回去對照那本一起看吧——另外一定得好好學英語了,要不它會成為我發展的瓶頸的。

    3 、設計模式

    ???? 只是將這些模式總結到了一個表里了,設計模式更是沒有編碼經驗就看不懂的東西,以前變成時還用過工廠方法,確實解決了不少問題。而且我越來越發現,很多高層次的書都離開不設計模式。比如那時候看《 Ajax in action 》,又有多少新東西呢,到后面就都是設計模式,工具的介紹,實際案例等等內容了,那時看過一篇文章說高手看書快,因為每本書的新東西都很有限,其實一點都不假,因為我已經看過《 Ajax 基礎教程》了,再加上《設計模式》,《 J2EE 核心模式》,一些工具(無非就是測試工具、調試工具、建模工具等),一些第三方類庫(框架),再加上點項目介紹,如果對這些部分多很了解,那么給它們套上一層 Ajax 的罩衣,不是很容易嗎? Ajax ,如果熟悉 Javascript ,熟悉 DOM ,熟悉 XML ,熟悉某種服務器端語言,熟悉 ActiveX 等模型,只需要學習一下 XMLHttp 對象不就 OK 了嗎?根本不需要太長時間,也沒有任何難度。然后還可能更進一步了解一些諸如企業級服務(遠程調用,事務處理等),管理方面的知識,不過如此嘛。當然這只是研究的一個方向,這里面一定存在很多最佳實踐,但是仔細研究那些最佳實踐甚至是模式,也只有一小部分是真正的創新,很少很少。其實學習 SOA 技術也不過如此,學學分析模式,分布式系統架構的知識, XML J2EE 發展史等等, SOA 也不是什么新的會讓 35 歲以后的人學不會的東西。這個話題激發了我想寫一篇怎么才不會 35 歲之后因為跟不上而被淘汰的文章了,好了,就寫這么多了。

    ?

    目的

    ?

    創建型

    結構型

    行為型

    ?

    范圍

    將對象的部分創建工作延遲到子類

    使用繼承機制組合

    使用繼承描述算法控制流

    Adapter (類)

    Factory Method

    Interpreter

    定義一個用于創建對象的接口,讓子類決定將哪一個類實例化, Factory Method 是一個類的實例化延遲到子類

    將一個類的接口轉換成客戶希望的另一個接口。 Adapter 模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作。

    給定一個語言,定義它的文法的一種表示,并定義一個解釋器,該解釋器使用該表示來解釋語言中的句子。

    Template Method

    定一個操作中的算法的骨架,而將一些步驟延遲到子類中。 Template Method 使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。

    ?

    對象

    將對象的部分創建工作延遲到另一個對象中

    描述對象的組裝方式

    描述一組對象怎樣協作完成單個對象所無法完成的任務

    Adapter

    對象

    Chain of Responsibility

    Abstract Factory

    為解除請求的發送者和接收者之間耦合,而使多個對象都有機會處理這個請求。將這些對象連成一條鏈,并沿著這條鏈傳遞該請求,直到有一個對象處理它。

    Command

    ?

    將一個請求封裝成為一個對象,從而使你可用不同的請求對客戶進行參數化;對請求排隊或記錄請求日志,以及支持可取消的操作。

    提供一個創建一系列相關或相互依賴對象的接口,而無須指定它們具體的類

    Iterator

    Bridge

    提供一種方法順序訪問一個聚合對象中各個元素,而又不需要暴露該對象的內部表示

    Mediator

    Builder

    抽象部分與它的實現部分分離,使他們都可以獨立的變化

    用一個中介對象來封裝一系列對象交互。中介者使各對象不需要顯示的相互引用,從而使其耦合松散,而且可以獨立的改變它們之間的交互。

    Memento

    將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示

    在不破壞封裝性的前提下,捕獲一個對象的內部狀態,并在該對象之外保存這個狀態。這樣以后就可以將該對象恢復到保存的狀態。

    Composite

    將對象組合成樹形結構以表示“部分 - 整體”的層次結構。 Composite 使得客戶對單個對象和復合對象的使用具有一致性。

    Prototype

    Observer

    定義對象間的一種一對多的依賴關系,以便當一個對象的狀態發生改變時,所有依賴它的對象都得到通知并自動刷新。

    用原型實例指定創建對象的種類,并且通過拷貝這個原型來創建新的對象

    Singleton

    State

    允許一個對象在其內部狀態改變時改變它的行為。對象看起來似乎修改了它所屬的類。

    保證一個類僅有一個實例,并提供一個訪問它的全局訪問點

    Decorator

    動態地給一個對象添加一些額外的職責。就擴展功能而言, Decorator 模式比生成子類方式更為靈活

    Strategy

    定義一系列算法,把它們一個個封裝起來,并且使它們可以互相替換。本模式使得算法的變化可獨立于使用它的客戶。

    Facade

    Visitor

    表示一個作用于某對象結構中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用于這

    ?

    些元素的新操作。

    為子系統中的一組接口提供一個一致的界面, Fa?ade 模式定義了一個高層接口,這個接口使得這一子系統更加容易使用

    Flyweight

    ?

    運用共享技術有效的支持大量細顆粒度的對象

    Proxy

    為其他對象提供一個代理以控制對這個對象的訪問

    ?

    posted on 2007-02-26 16:57 零雨其蒙 閱讀(1616) 評論(2)  編輯  收藏 所屬分類: 學習筆記

    評論

    # re: 假期學習筆記(二)  回復  更多評論   

    佩服!
    有些東西,自己知道就成了,很少把它們形諸文字,所以我佩服你!
    另: Lazy Class還真的到處都是!
    應該提升程序員自己的責任感:保證自己寫的代碼句句都是有用的。
    2007-02-27 18:12 | yielruse@126.com

    # re: 假期學習筆記(二)  回復  更多評論   

    其實很多時候就是程序員太懶了,否則,一般的程序員避開這些bad smell是不難的
    2007-03-09 21:35 | ouyida3
    主站蜘蛛池模板: 亚洲AV无码专区在线观看成人| 一级特黄色毛片免费看| 日韩人妻无码免费视频一区二区三区| 欧洲亚洲综合一区二区三区| 日韩亚洲一区二区三区| 手机在线看永久av片免费| 春暖花开亚洲性无区一区二区 | 亚洲欧洲国产精品香蕉网| 19禁啪啪无遮挡免费网站| 国产午夜亚洲精品不卡| 亚洲精品自产拍在线观看动漫| 日韩免费在线观看| 一个人看的www免费视频在线观看 一个人免费视频观看在线www | 日本在线免费观看| 亚洲AV无码AV吞精久久| 亚洲黄色网址在线观看| 免费夜色污私人影院在线观看| 曰批全过程免费视频播放网站| 国产av无码专区亚洲av毛片搜| 91亚洲自偷手机在线观看| 亚洲国产一成久久精品国产成人综合| 99精品热线在线观看免费视频 | 2021在线观看视频精品免费| 免费人成大片在线观看播放电影| 亚洲视频一区在线观看| 久久影视国产亚洲| 免费高清在线爱做视频| 51在线视频免费观看视频| 中国极品美軳免费观看| 色婷婷亚洲一区二区三区| 亚洲午夜国产精品无卡| 亚洲AV午夜成人片| 亚洲精品无码av天堂| 日韩在线免费播放| 三年片在线观看免费观看高清电影 | 亚洲国产成人超福利久久精品| 亚洲日韩精品一区二区三区无码 | 无人在线观看完整免费版视频| 95免费观看体验区视频| 在线免费观看伊人三级电影| 免费无码一区二区|