設計模式和代碼重構,代碼重構五大原則

本文目錄一覽:

常見代碼重構技巧(非常實用)

1_代碼重構漫畫.jpeg

項目在不斷演進過程中,代碼不停地在堆砌。如果沒有人為代碼的質量負責,代碼總是會往越來越混亂的方向演進。當混亂到一定程度之後,量變引起質變,項目的維護成本已經高過重新開發一套新代碼的成本,想要再去重構,已經沒有人能做到了。

造成這樣的原因往往有以下幾點:

對於此類問題,業界已有有很好的解決思路:通過持續不斷的重構將代碼中的「壞味道」清除掉。

重構一書的作者Martin Fowler對重構的定義:

根據重構的規模可以大致分為大型重構和小型重構:

大型重構 :對頂層代碼設計的重構,包括:系統、模塊、代碼結構、類與類之間的關係等的重構,重構的手段有:分層、模塊化、解耦、抽象可復用組件等等。這類重構的工具就是我們學習過的那些設計思想、原則和模式。這類重構涉及的代碼改動會比較多,影響面會比較大,所以難度也較大,耗時會比較長,引入bug的風險也會相對比較大。

小型重構 :對代碼細節的重構,主要是針對類、函數、變量等代碼級別的重構,比如規範命名和注釋、消除超大類或函數、提取重複代碼等等。小型重構更多的是使用統一的編碼規範。這類重構要修改的地方比較集中,比較簡單,可操作性較強,耗時會比較短,引入bug的風險相對來說也會比較小。什麼時候重構 新功能開發、修bug或者代碼review中出現「代碼壞味道」,我們就應該及時進行重構。持續在日常開發中進行小重構,能夠降低重構和測試的成本。

2_代碼常見問題.png

代碼重複

方法過長

過大的類

邏輯分散

嚴重的情結依戀

數據泥團/基本類型偏執

不合理的繼承體系

過多的條件判斷

過長的參數列

臨時變量過多

令人迷惑的暫時字段

純數據類

不恰當的命名

過多的注釋

3_代碼質量如何衡量.jpg

代碼質量的評價有很強的主觀性,描述代碼質量的詞彙也有很多,比如可讀性、可維護性、靈活、優雅、簡潔。這些詞彙是從不同的維度去評價代碼質量的。其中,可維護性、可讀性、可擴展性又是提到最多的、最重要的三個評價標準。

要寫出高質量代碼,我們就需要掌握一些更加細化、更加能落地的編程方法論,這就包含面向對象設計思想、設計原則、設計模式、編碼規範、重構技巧等。

4_SOLID原則.png

一個類只負責完成一個職責或者功能,不要存在多於一種導致類變更的原因。

單一職責原則通過避免設計大而全的類,避免將不相關的功能耦合在一起,來提高類的內聚性。同時,類職責單一,類依賴的和被依賴的其他類也會變少,減少了代碼的耦合性,以此來實現代碼的高內聚、松耦合。但是,如果拆分得過細,實際上會適得其反,反倒會降低內聚性,也會影響代碼的可維護性。

添加一個新的功能,應該是通過在已有代碼基礎上擴展代碼(新增模塊、類、方法、屬性等),而非修改已有代碼(修改模塊、類、方法、屬性等)的方式來完成。

開閉原則並不是說完全杜絕修改,而是以最小的修改代碼的代價來完成新功能的開發。

很多設計原則、設計思想、設計模式,都是以提高代碼的擴展性為最終目的的。特別是 23 種經典設計模式,大部分都是為了解決代碼的擴展性問題而總結出來的,都是以開閉原則為指導原則的。最常用來提高代碼擴展性的方法有:多態、依賴注入、基於接口而非實現編程,以及大部分的設計模式(比如,裝飾、策略、模板、職責鏈、狀態)。

子類對象(object of subtype/derived class)能夠替換程序(program)中父類對象(object of base/parent class)出現的任何地方,並且保證原來程序的邏輯行為(behavior)不變及正確性不被破壞。

子類可以擴展父類的功能,但不能改變父類原有的功能

調用方不應該依賴它不需要的接口;一個類對另一個類的依賴應該建立在最小的接口上。接口隔離原則提供了一種判斷接口的職責是否單一的標準:通過調用者如何使用接口來間接地判定。如果調用者只使用部分接口或接口的部分功能,那接口的設計就不夠職責單一。

高層模塊不應該依賴低層模塊,二者都應該依賴其抽象;抽象不應該依賴細節,細節應該依賴抽象。

一個對象應該對其他對象保持最少的了解

盡量使用合成/聚合的方式,而不是使用繼承。

單一職責原則告訴我們實現類要職責單一;里氏替換原則告訴我們不要破壞繼承體系;依賴倒置原則告訴我們要面向接口編程;接口隔離原則告訴我們在設計接口的時候要精簡單一;迪米特法則告訴我們要降低耦合。而開閉原則是總綱,告訴我們要對擴展開放,對修改關閉。

image.png

模塊結構說明

代碼開發要遵守各層的規範,並注意層級之間的依賴關係。

多個方法代碼重複、方法中代碼過長或者方法中的語句不在一個抽象層級。

方法是代碼復用的最小粒度,方法過長不利於復用,可讀性低,提煉方法往往是重構工作的第一步。

意圖導向編程 :把處理某件事的流程和具體做事的實現方式分開。

將函數放進一個單獨對象中,如此一來局部變量就變成了對象內的字段。然後你可以在同一個對象中將這個大型函數分解為多個小型函數。

方法參數比較多時,將參數封裝為參數對象

任何有返回值的方法,都不應該有副作用

臨時變量僅使用一次或者取值邏輯成本很低的情況下

將複雜表達式(或其中一部分)的結果放進一個臨時變量,以此變量名稱來解釋表達式用途

把複雜的條件表達式拆分成多個條件表達式,減少嵌套。嵌套了好幾層的if – then-else語句,轉換為多個if語句

當出現大量類型檢查和判斷時,if else(或switch)語句的體積會比較臃腫,這無疑降低了代碼的可讀性。 另外,if else(或switch)本身就是一個「變化點」,當需要擴展新的類型時,我們不得不追加if else(或switch)語句塊,以及相應的邏輯,這無疑降低了程序的可擴展性,也違反了面向對象的開閉原則。

非正常業務狀態的處理,使用拋出異常的方式代替返回錯誤碼

某一段代碼需要對程序狀態做出某種假設,以斷言明確表現這種假設。

當使用一個方法返回的對象時,而這個對象可能為空,這個時候需要對這個對象進行操作前,需要進行判空,否則就會報空指針。當這種判斷頻繁的出現在各處代碼之中,就會影響代碼的美觀程度和可讀性,甚至增加Bug的幾率。

空引用的問題在Java中無法避免,但可以通過代碼編程技巧(引入空對象)來改善這一問題。

根據單一職責原則,一個類應該有明確的責任邊界。但在實際工作中,類會不斷的擴展。當給某個類添加一項新責任時,你會覺得不值得分離出一個單獨的類。於是,隨着責任不斷增加,這個類包含了大量的數據和函數,邏輯複雜不易理解。

此時你需要考慮將哪些部分分離到一個單獨的類中,可以依據高內聚低耦合的原則。如果某些數據和方法總是一起出現,或者某些數據經常同時變化,這就表明它們應該放到一個類中。另一種信號是類的子類化方式:如果你發現子類化隻影響類的部分特性,或者類的特性需要以不同方式來子類化,這就意味着你需要分解原來的類。

繼承使實現代碼重用的有力手段,但這並非總是完成這項工作的最佳工具,使用不當會導致軟件變得很脆弱。與方法調用不同的是,繼承打破了封裝性。子類依賴於其父類中特定功能的實現細節,如果父類的實現隨着發行版本的不同而變化,子類可能會遭到破壞,即使他的代碼完全沒有改變。

舉例說明,假設有一個程序使用HashSet,為了調優該程序的性能,需要統計HashSet自從它創建以來添加了多少個元素。為了提供該功能,我們編寫一個HashSet的變體。

通過在新的類中增加一個私有域,它引用現有類的一個實例,這種設計被稱為組合,因為現有的類變成了新類的一個組件。這樣得到的類將會非常穩固,它不依賴現有類的實現細節。即使現有的類添加了新的方法,也不會影響新的類。許多設計模式使用就是這種套路,比如代理模式、裝飾者模式

繼承與組合如何取捨

Java提供了兩種機制,可以用來定義允許多個實現的類型:接口和抽象類。自從Java8為接口增加缺省方法(default method),這兩種機制都允許為實例方法提供實現。主要區別在於,為了實現由抽象類定義的類型,類必須稱為抽象類的一個子類。因為Java只允許單繼承,所以用抽象類作為類型定義受到了限制。

接口相比於抽象類的優勢:

接口雖然提供了缺省方法,但接口仍有有以下局限性:

接口缺省方法的設計目的和優勢在於:

為了接口的演化

可以減少第三方工具類的創建

可以避免創建基類

由於接口的局限性和設計目的的不同,接口並不能完全替換抽象類。但是通過對接口提供一個抽象的骨架實現類,可以把接口和抽象類的優點結合起來。 接口負責定義類型,或許還提供一些缺省方法,而骨架實現類則負責實現除基本類型接口方法之外,剩下的非基本類型接口方法。擴展骨架實現佔了實現接口之外的大部分工作。這就是模板方法(Template Method)設計模式。

Image [5].png

接口Protocol:定義了RPC協議層兩個主要的方法,export暴露服務和refer引用服務

抽象類AbstractProtocol:封裝了暴露服務之後的Exporter和引用服務之後的Invoker實例,並實現了服務銷毀的邏輯

具體實現類XxxProtocol:實現export暴露服務和refer引用服務具體邏輯

由於為了保持Java代碼的兼容性,支持和原生態類型轉換,並使用擦除機制實現的泛型。但是使用原生態類型就會失去泛型的優勢,會受到編譯器警告。

每一條警告都表示可能在運行時拋出ClassCastException異常。要盡最大的努力去消除這些警告。如果無法消除但是可以證明引起警告的代碼是安全的,就可以在儘可能小的範圍中,使用@SuppressWarnings(“unchecked”)註解來禁止警告,但是要把禁止的原因記錄下來。

參數化類型不支持協變的,即對於任何兩個不同的類型Type1和Type2而言,List既不是List的子類型,也不是它的超類。為了解決這個問題,提高靈活性,Java提供了一種特殊的參數化類型,稱作有限制的通配符類型,即List? extends E和List? super E。使用原則是producer-extends,consumer-super(PECS)。如果即是生產者,又是消費者,就沒有必要使用通配符了。

還有一種特殊的無限制通配符List?,表示某種類型但不確定。常用作泛型的引用,不可向其添加除Null以外的任何對象。

嵌套類(nested class)是指定義在另一個類的內部的類。 嵌套類存在的目的只是為了它的外部類提供服務,如果其他的環境也會用到的話,應該成為一個頂層類(top-level class)。 嵌套類有四種:靜態成員類(static member class)、非靜態成員類(nonstatic member class)、匿名類(anonymous class)和 局部類(local class)。除了第一種之外,其他三種都稱為內部類(inner class)。

總而言之,這四種嵌套類都有自己的用途。假設這個嵌套類屬於一個方法的內部,如果只需要在一個地方創建實例,並且已經有了一個預置的類型可以說明這個類的特徵,就要把它做成匿名類。如果一個嵌套類需要在單個方法之外仍然可見,或者它太長了,不適合放在方法內部,就應該使用成員類。如果成員類的每個實例都需要一個指向其外圍實例的引用,就要把成員類做成非靜態的,否則就做成靜態的。

通過對常見場景的代碼邏輯進行抽象封裝,形成相應的模板工具類,可以大大減少重複代碼,專註於業務邏輯,提高代碼質量。

面向對象編程相對於面向過程,多了實例化這一步,而對象的創建必須要指定具體類型。我們常見的做法是「哪裡用到,就在哪裡創建」,使用實例和創建實例的是同一段代碼。這似乎使代碼更具有可讀性,但是某些情況下造成了不必要的耦合。

對於頂層的(非嵌套的)類和接口,只有兩種的訪問級別:包級私有的(沒有public修飾)和公有的(public修飾)。

對於成員(實例/域、方法、嵌套類和嵌套接口)由四種的訪問級別,可訪問性如下遞增:

正確地使用這些修飾符對於實現信息隱藏是非常關鍵的,原則就是:儘可能地使每個類和成員不被外界訪問(私有或包級私有)。這樣好處就是在以後的發行版本中,可以對它進行修改、替換或者刪除,而無須擔心會影響現有的客戶端程序。

不可變類是指其實例不能被修改的類。每個實例中包含的所有信息都必須在創建該實例時提供,並在對象的整個生命周期內固定不變。不可變類好處就是簡單易用、線程安全、可自由共享而不容易出錯。Java平台類庫中包含許多不可變的類,比如String、基本類型包裝類、BigDecimal等。

為了使類成為不可變,要遵循下面五條規則:

可變性最小化的一些建議:

TDD的最終目標是整潔可用的代碼(clean code that works)。大多數的開發者大部分時間無法得到整潔可用的代碼。辦法是分而治之。首先解決目標中的「可用」問題,然後再解決「代碼的整潔」問題。這與體系結構驅動(architecture-driven)的開發相反。

採用TDD另一個好處就是讓我們擁有一套伴隨代碼產生的詳盡的自動化測試集。將來無論出於任何原因(需求、重構、性能改進)需要對代碼進行維護時,在這套測試集的驅動下工作,我們代碼將會一直是健壯的。

Image [6].png

添加一個測試 – 運行所有測試並檢查測試結果 – 編寫代碼以通過測試 – 運行所有測試且全部通過 – 重構代碼,以消除重複設計,優化設計結構

作者:VectorJin

請教下各位前輩,java的設計模式是先了解再做項目的好還是想做項目等熟悉了之後再用設計模式將代碼重構好?

先了解再做項目!

軟件項目重在設計,如果沒有一個結構化、系統化的設計就開始項目開發,是不明智的,到最後可能會變成一個龐大、複雜、難於維護的境地,而等項目完成了,在用設計模式重構,還會增加軟件測試工作量,對於之前的內容都要全部重新測試,浪費資源,得不償失。

對於設計模式的學習,推薦一本經典書籍《軟件秘笈-設計模式那點事》,相信會對你有幫助的!祝你早日學會設計模式!

如何重構代碼

先從接觸過的幾個老項目經歷來談談,對於老項目來說,大家在初步接觸的過程中,大多總是抱着抵觸的情緒,甚至有些是蔑視。總喜歡對以前的代碼挑出一大堆的問題,接着就開始抱怨代碼、抱怨以前的開發人員,經過一段時間鬱悶的抱怨階段後,處於職業的責任心,就很想去改變這一切,希望把自己認為好的方式給帶進來,於是接下來的工作就是重構代碼了。 這也許大多數開發人員都經歷過,這種經歷是辛酸的(因為重構工作雖然重要,但是得不到過多的認可,目前國內關注的是可用性,對於代碼質量並沒有得到應有的重視),也是甜蜜的(風雨之後總會有彩虹)。對於年輕的開發人員來說,見到彩虹的過程是痛苦、漫長地。他們都是在失敗中成長,這些失敗除了經驗外,主要是由於太急功儘力了,盲目的重構! 盲目主要體現在: 1、在還沒有對系統整體架構有個清晰認識的時候,就想用自認為新的技術或架構來替換。 2、根本不分析現有系統架構或程序存在的弊端,只是一味地談設計模式,以設計模式中固有的一套來重構(在重構中,它只作為一個參考,而不是一個依據。) 3、重構比較隨性,每個版本的開發都跳出架構之外隨意帶入新的設計思想 這種盲目重構後給系統會帶來更多問題: 你會發現當你重構完後你的系統運行效率變低了, 系統中同時存在多種思想,新加入人員更難接手, 由於你沒有完全了解系統,反而在你的重構當中帶來了很多重複代碼, 最悲劇的是你重構後的代碼也被其他人當成垃圾,而進行重構。 那麼我們怎麼消除盲目呢!? 首先,了解目前項目是否存在問題,存在什麼問題,這些問題是否能通過重構來解決,如果能,才進行重構,你的重構時間是需要公司給的,老闆不會因為你說依賴性強偶合性低就同意的,你必須要通過問題來讓他認識,關鍵的是只有通過問題才能得到重構時間和資源,並且你的工作才能得到認可,這是一個很現實的情況。 接下來,你要確定重構的對象,是針對架構還是局部代碼,並且去設定一個理想的目標(為什麼是理想的?因為我們不可能一步到位,理想和現實是有差距的,但是我們要做的是儘力去往理想上靠攏)。 如果是針對架構進行重構,那麼這可不是一件輕鬆的事情,再真正開始之前需要做到以下幾點: 1、全面的了解系統的過去,包括以前的架構/技術背景、業務需求 2、分析以前架構的問題,例如:可維護性低、在哪個方面已經不滿足現有需求等等 3、查看至少80%的核心代碼,最好有一定時間的真實在以前代碼基礎上編碼的經歷 做到上面幾點就是為了保證你能有一個清晰的認識,做到知己知彼。接下來可以進入實質階段了嗎?不行,還少了一個很重要的東西,重構計劃! 這種大範圍的重構,在真實情況下,一般老闆給予的時間和重構真正所需用的時間相差很大,所以重構的工作是需要往後延遲的,那麼就會出現又要重構又要進行新需求的開發;還有這項工作不是一個人的事情,是一個團對,既然涉及到多人合作,除了共同的目標外,還需要有一定的評審機制,這是為了保證重構的方向一致,等等,在這些因素下要做好重構,就是需要重構計劃的理由。 針對局部代碼進行重構來說,也許會簡單的許多,不過需要注意的地方是,你一定要符合現有架構的思想,在它的範圍之內去思考。 其實這種方式的重構大多就是提取方法,或者是以真實業務流程的思路去重構現有的代碼執行流程,以便易於理解,或者是降低程序之間的依賴性。要做到這些有個很重要的思維方式: 1、善於從某個事物中分析出什麼是事物的本質和什麼是事物的外部環境。 2、從很多不同事物中去發現共同點,並對這些共同點進行抽象化(舉個簡單的例子:對於寶馬和奧迪,你應該把他們抽象化為汽車)。 為什麼這樣說,因為這些能帶來重構代碼所需要的: 1、在寫代碼過程中降低了依賴性, 2、抽象化的事物復用性更強 做好上述的所有就表示重構完成了嗎!不可能,這只是一個好的開始而已,我們要做到持續重構,就像敏捷中提到的。 也許有的人認為不現實,因為項目經理不會在每個版本周期內給出這個時間,其實,我就納悶了,為什麼不給?!不給的原因一定在你,如果你期望是一周或者更久,那麼誰都不會同意,一周的時間都基本都能做完一個版本的設計了,重構還需要這麼久,如果真的需要就說明你前期的設計很差!我所希望的時間是兩天左右,因為這隻局限於很小範圍內的變動。 如果你們很好的做這些,那麼你的項目可維護性一定很好,並且加入你的項目會是一件愉快的事情,這並不是什麼理想的事情,只要你持之以恆地去做,實現起來其實很簡單。 架構重構:1、重構計劃代碼重構:2、提取獲取交集的算法3、簡單、靈活地實現對象複製

代碼重構的概述

重構(),通過調整程序代碼改善軟件的質量、性能,使其程序的設計模式和架構更趨合理,提高軟件的擴展性和維護性。 也許有人會問,為什麼不在項目開始時多花些時間把設計做好,而要以後花時間來重構呢?要知道一個完美得可以預見未來任何變化的設計,或一個靈活得可以容納任何擴展的設計是不存在的。系統設計人員對即將着手的項目往往只能從大方向予以把控,而無法知道每個細枝末節,其次永遠不變的就是變化,提出需求的用戶往往要在軟件成型後,才開始品頭論足,系統設計人員畢竟不是先知先覺的神仙,功能的變化導致設計的調整再所難免。所以測試為先,持續重構作為良好開發習慣被越來越多的人所採納,測試和重構像黃河的護堤,成為保證軟件質量的法寶。 在不改變系統功能的情況下,改變系統的實現方式。為什麼要這麼做?投入精力不用來滿足客戶關心的需求,而是僅僅改變了軟件的實現方式,這是否是在浪費客戶的投資呢?

重構的重要性要從軟件的生命周期說起。軟件不同與普通的產品,他是一種智力產品,沒有具體的物理形態。一個軟件不可能發生物理損耗,界面上的按鈕永遠不會因為按動次數太多而發生接觸不良。那麼為什麼一個軟件製造出來以後,卻不能永遠使用下去呢?

對軟件的生命造成威脅的因素只有一個:需求的變更。一個軟件總是為解決某種特定的需求而產生,時代在發展,客戶的業務也在發生變化。有的需求相對穩定一些,有的需求變化的比較劇烈,還有的需求已經消失了,或者轉化成了別的需求。在這種情況下,軟件必須相應的改變。

考慮到成本和時間等因素,當然不是所有的需求變化都要在軟件系統中實現。但是總的說來,軟件要適應需求的變化,以保持自己的生命力。

這就產生了一種糟糕的現象:軟件產品最初製造出來,是經過精心的設計,具有良好架構的。但是隨着時間的發展、需求的變化,必須不斷的修改原有的功能、追加新的功能,還免不了有一些缺陷需要修改。為了實現變更,不可避免的要違反最初的設計構架。經過一段時間以後,軟件的架構就千瘡百孔了。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),你必須同時維護新舊兩個接口,直到你的所有用戶都有時間對這個變化做出反應。幸運的是這不太困難。你通常都有辦法把事情組織好,讓舊接口繼續工作。請盡量這麼做:讓舊接口調用新接口。當你要修改某個函數名稱時,請留下舊函數,讓它調用新函數。千萬不要拷貝函數實現碼,那會讓你陷入「重複代碼」(duplicated 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謝絕參加)認真推測過:我們瞭若指掌的這個程序中可能有什麼錯誤?我們甚至憑空做了些改進設計,卻沒有先對系統的真實情況進行量測。

我們完全錯了。除了一場很有趣的交談,我們什麼好事都沒做。

教訓:哪怕你完全了解系統,也請實際量測它的性能,不要臆測。臆測會讓你學到一些東西,但十有八九你是錯的。

什麼是代碼重構?

代碼重構(英語:Code refactoring)重構就是在不改變軟件系統外部行為的前提下,改善它的內部結構。

軟件重構需要藉助工具完成,重構工具能夠修改代碼同時修改所有引用該代碼的地方。在極限編程的方法學中,重構需要單元測試來支持。

java重構:指程序員對已有程序在盡量不改變接口的前提下,進行重新編寫代碼的工作,一般有以下幾方面:

1、去除已知bug。

2、提高程序運行效率。

3、增加新的功能。

重構舉例:(簡化代碼、提升效率)

重構前:

if(list != null  list.size()  0){

for(int i = 0; i  list.size(); i++){

//skip…

}

}

重構後

if(list != null){

for(int i = 0, len = list.size(); i  len; i++){

//skip…

}

}

何時着手重構(Refactoring)

新官上任三把火,開始一個全新??、腳不停蹄、加班加點,一支聲勢浩大的千軍萬”碼”夾裹着程序員激情和扣擊鍵盤的鳴金奮力前行,勢如破竹,攻城掠地,直指”黃龍府”。

開發經理是這支浩浩湯湯代碼隊伍的統帥,他負責這支隊伍的命運,當齊桓公站在山頂上看到管仲訓練的隊伍整齊劃一地前進時,他感嘆說”我有這樣一支軍隊哪裡還怕沒有勝利呢?”。但很遺憾,你手中的這支隊伍原本只是散兵游勇,在前進中招兵買馬,不斷壯大,所以隊伍變形在所難免。當開發經理髮覺隊伍變形時,也許就是克制住攻克前方山頭的誘惑,停下腳步整頓隊伍的時候了。

Kent Beck提出了”代碼壞味道”的說法,和我們所提出的”隊伍變形”是同樣的意思,隊伍變形的信號是什麼呢?以下列述的代碼癥狀就是”隊伍變形”的強烈信號:

·代碼中存在重複的代碼

中國有118 家整車生產企業,數量幾乎等於美、日、歐所有汽車廠家數之和,但是全國的年產量卻不及一個外國大汽車公司的產量。重複建設只會導致效率的低效和資源的浪費。

程序代碼更是不能搞重複建設,如果同一個類中有相同的代碼塊,請把它提煉成類的一個獨立方法,如果不同類中具有相同的代碼,請把它提煉成一個新類,永遠不要重複代碼。

·過大的類和過長的方法

過大的類往往是類抽象不合理的結果,類抽象不合理將降低了代碼的復用率。方法是類王國中的諸侯國,諸侯國太大勢必動搖中央集權。過長的方法由於包含的邏輯過於複雜,錯誤機率將直線上升,而可讀性則直線下降,類的健壯性很容易被打破。當看到一個過長的方法時,需要想辦法將其劃分為多個小方法,以便於分而治之。

·牽一毛而需要動全身的修改

當你發現修改一個小功能,或增加一個小功能時,就引發一次代碼地震,也許是你的設計抽象度不夠理想,功能代碼太過分散所引起的。

·類之間需要過多的通訊

A類需要調用B類的過多方法訪問B的內部數據,在關係上這兩個類顯得有點狎昵,可能這兩個類本應該在一起,而不應該分家。

·過度耦合的信息鏈

“計算機是這樣一門科學,它相信可以通過添加一個中間層解決任何問題”,所以往往中間層會被過多地追加到程序中。如果你在代碼中看到需要獲取一個信息,需要一個類的方法調用另一個類的方法,層層掛接,就象輸油管一樣節節相連。這往往是因為銜接層太多造成的,需要查看就否有可移除的中間層,或是否可以提供更直接的調用方法。

·各立山頭幹革命

如果你發現有兩個類或兩個方法雖然命名不同但卻擁有相似或相同的功能,你會發現往往是因為開發團隊協調不夠造成的。筆者曾經寫了一個頗好用的字符串處理類,但因為沒有及時通告團隊其他人員,後來發現項目中居然有三個字符串處理類。革命資源是珍貴的,我們不應各立山頭幹革命。

·不完美的設計

在筆者剛完成的一個比對報警項目中,曾安排阿朱開發報警模塊,即通過Socket向指定的短訊平台、語音平台及客戶端報警器插件發送報警報文信息,阿朱出色地完成了這項任務。後來用戶又提出了實時比對的需求,即要求第三方系統以報文形式向比對報警系統發送請求,比對報警系統接收並響應這個請求。這又需要用到Socket報文通訊,由於原來的設計沒有將報文通訊模塊獨立出來,所以無法復用阿朱開發的代碼。後來我及時調整了這個設計,新增了一個報文收發模塊,使系統所有的對外通訊都復用這個模塊,系統的整體設計也顯得更加合理。

每個系統都或多或少存在不完美的設計,剛開始可能注意不到,到後來才會慢慢凸顯出來,此時唯有勇於更改才是最好的出路。

·缺少必要的注釋

雖然許多軟件工程的書籍常提醒程序員需要防止過多注釋,但這個擔心好象並沒有什麼必要。往往程序員更感興趣的是功能實現而非代碼注釋,因為前者更能帶來成就感,所以代碼注釋往往不是過多而是過少,過於簡單。人的記憶曲線下降的坡度是陡得嚇人的,當過了一段時間後再回頭補註釋時,很容易發生”提筆忘字,愈言且止”的情形。

曾在網上看到過微軟的代碼注釋,其詳盡程度讓人嘆為觀止,也從中體悟到了微軟成功的一個經驗。

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/185511.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-11-26 12:19
下一篇 2024-11-26 12:19

相關推薦

  • Python周杰倫代碼用法介紹

    本文將從多個方面對Python周杰倫代碼進行詳細的闡述。 一、代碼介紹 from urllib.request import urlopen from bs4 import Bea…

    編程 2025-04-29
  • Python字符串寬度不限制怎麼打代碼

    本文將為大家詳細介紹Python字符串寬度不限制時如何打代碼的幾個方面。 一、保持代碼風格的統一 在Python字符串寬度不限制的情況下,我們可以寫出很長很長的一行代碼。但是,為了…

    編程 2025-04-29
  • Python基礎代碼用法介紹

    本文將從多個方面對Python基礎代碼進行解析和詳細闡述,力求讓讀者深刻理解Python基礎代碼。通過本文的學習,相信大家對Python的學習和應用會更加輕鬆和高效。 一、變量和數…

    編程 2025-04-29
  • 倉庫管理系統代碼設計Python

    這篇文章將詳細探討如何設計一個基於Python的倉庫管理系統。 一、基本需求 在着手設計之前,我們首先需要確定倉庫管理系統的基本需求。 我們可以將需求分為以下幾個方面: 1、庫存管…

    編程 2025-04-29
  • Python滿天星代碼:讓編程變得更加簡單

    本文將從多個方面詳細闡述Python滿天星代碼,為大家介紹它的優點以及如何在編程中使用。無論是剛剛接觸編程還是資深程序員,都能從中獲得一定的收穫。 一、簡介 Python滿天星代碼…

    編程 2025-04-29
  • 寫代碼新手教程

    本文將從語言選擇、學習方法、編碼規範以及常見問題解答等多個方面,為編程新手提供實用、簡明的教程。 一、語言選擇 作為編程新手,選擇一門編程語言是很關鍵的一步。以下是幾個有代表性的編…

    編程 2025-04-29
  • Python實現簡易心形代碼

    在這個文章中,我們將會介紹如何用Python語言編寫一個非常簡單的代碼來生成一個心形圖案。我們將會從安裝Python開始介紹,逐步深入了解如何實現這一任務。 一、安裝Python …

    編程 2025-04-29
  • 怎麼寫不影響Python運行的長段代碼

    在Python編程的過程中,我們不可避免地需要編寫一些長段代碼,包括函數、類、複雜的控制語句等等。在編寫這些代碼時,我們需要考慮代碼可讀性、易用性以及對Python運行性能的影響。…

    編程 2025-04-29
  • 北化教務管理系統介紹及開發代碼示例

    本文將從多個方面對北化教務管理系統進行介紹及開發代碼示例,幫助開發者更好地理解和應用該系統。 一、項目介紹 北化教務管理系統是一款針對高校學生和教職工的綜合信息管理系統。系統實現的…

    編程 2025-04-29
  • Python愛心代碼動態

    本文將從多個方面詳細闡述Python愛心代碼動態,包括實現基本原理、應用場景、代碼示例等。 一、實現基本原理 Python愛心代碼動態使用turtle模塊實現。在繪製一個心形的基礎…

    編程 2025-04-29

發表回復

登錄後才能評論