敏捷的編碼

敏捷編碼

代碼要清晰地表達意圖

開發代碼時,應該更注重可讀性,而不是隻圖本身方便。代碼閱讀的次數要遠遠超過編寫的次數,因此在編寫的時候值得花點功夫讓它讀起來更加簡單。java

PIE原則(Program Intently and Expressively)
代碼必須明確說出你的意圖,並且必須富有表達力。這樣可讓代碼更易於被別人閱讀和理解。代碼不讓人迷惑,也就減小了發生潛在錯誤的可能。一言以蔽之,代碼應意圖清晰,表達明確。程序員

在編寫代碼時,應該使用語言特性來提高表現力。使用方法名來傳達意向,對方法參數的命名要幫助讀者理解背後的想法。異常傳達的信息是哪些可能會出問題,以及如何進行防護式編程,要正確地使用和命名異常。好的編碼規範可讓代碼變得易於理解,同時減小沒必要要的註釋和文檔。編程

具體技巧

  • 如今對你顯而易見的事情,對別人可能並不是如此,對於一年之後的你來講,也不必定顯而易見。

用代碼溝通

一般程序員都很討厭寫文檔,這是由於大部分文檔都與代碼沒有關係,而且愈來愈難以保證其符合目前的最新情況。這不僅違反了DRY原則(Don't Repeat Yourself),還會產生令人誤解的文檔,這還不如沒有文檔。工具

創建代碼文檔無外乎兩種方式:利用代碼自己;利用註釋來溝通代碼以外的問題。性能

Don't comment to cover up.
若是必須通讀一個方法的代碼才能瞭解它作了什麼,那麼開發人員先要投入大量的時間和精力才能使用它。反過來說,只需短短几行註釋說明方法行爲,就可讓生活變得輕鬆許多。開發人員能夠很快了解到它的意圖、它的期待結果,以及應該注意之處——這可省了你很多勁兒。單元測試

應該文檔化你全部的代碼嗎?在某種程度上說,是的。但這並不意味着要註釋絕大部分代碼,特別是在方法體內部。源代碼能夠被讀懂,不是由於其中的註釋,而應該是因爲它自己優雅而清晰——變量名運用正確、空格使用獲得、邏輯分離清晰,以及表達式很是簡潔。測試

如何命名很重要。程序元素的命名是代碼讀者必讀的部分。經過使用細心挑選的名稱,能夠向閱讀者傳遞大量的意圖和信息。反過來說,使用人造的命名範式(好比如今已經無人問津的匈牙利表示法)會讓代碼難以閱讀和理解。這些範式中包括的底層數據類型信息,會硬變嗎在變量名和方法名中,造成脆弱、僵化的代碼,並會在未來形成麻煩。使用細心挑選的名稱和清晰的執行路徑,代碼幾乎不須要註釋。優化

對於顯而易見的代碼增長註釋,也會有一樣的問題。這種註釋很常見——一般是由過於熱心的IDE插入的。許多註釋沒有傳遞任何有意義的信息,這種註釋只會分散注意力,並且很容易失去時效性。編碼

對於類中的每一個方法可能要說明下列信息:設計

  • 目的:爲何須要這個方法?
  • 需求(前置條件):方法須要什麼樣的輸入,對象必須處於何種狀態,才能讓這方法工做?
  • 承諾(後置條件):方法成功執行後,對象如今處於什麼狀態,有哪些返回值?
  • 異常:可能會發生什麼樣的問題?會拋出什麼樣的異常?

要感謝如RDoc、javadoc和ndoc這樣的工具,使用它們能夠很方便地直接從代碼註釋建立有用的、格式優美的文檔。這些工具抽取註釋,並生成樣式漂亮且帶有超連接的HTML輸出。

具體技巧

  • 在代碼能夠傳遞意圖的地方不要使用註釋
  • 解釋代碼作了什麼的註釋用處不那麼大。相反,註釋要說明爲何會這樣寫代碼。
  • 當重寫方法時,保留描述原有方法意圖和約束的註釋。

動態評估取捨

對任何單個因素如此專斷地強調,而不考慮它是不是項目成功的必要因素,必然致使災難的發生。若是應用的性能已經足夠好了,還有必要繼續投入精力讓其運行的更快一點嗎?大概不用了吧。一個應用還有許多其餘方面的因素一樣重要。與其花費時間去提高千分之一的性能表現,也許減小開發投入,下降成本,並儘快讓應用程序上市銷售更有價值。

問題的關鍵是要多長個心眼兒,而不是總按照習慣的思路去解決問題。若是團隊認爲性能上還有提高的空間,那麼就去諮詢一下利益相關者,讓他們決定應將重點放在哪裏。沒有適宜全部情況的最佳解決方案。你必須對手上的問題進行評估,並選出最合適的解決方案。每一個設計都是針對特定問題的一一隻有明確地進行評估和權衡,才能獲得更好的解決方案。

具體技巧

  • 若是如今投入額外的資源和精力,是爲了未來可能獲得的好處,要確認投入必定要獲得回報(大部分狀況下,是不會有回報的)
  • 真正的高性能系統,從一開始設計時就在向這個方向努力。
  • 過早的優化是萬惡之源。
  • 過去用過的解決方案對當前的問題可能適用,也可能不適用。不要事先預設結論,先看看如今是什麼情況。

增量式編程

若是不對本身編寫的代碼進行測試,保證沒有問題,就不要連續幾個小時,甚至連續幾分鐘進行編程。相反,應該採用增量式的編程方式。增量式編程能夠精煉並結構化你的代碼。代碼被複雜化、變成一團亂麻的概率減小了。所開發的代碼基於即時的反饋,這些反饋來自以小步幅方式編寫代碼和測試的過程。

採起增量式編程和測試,會傾向於建立更小的方法和更具內聚性的類。你不是在埋頭盲目地編寫一大堆代碼。相反,你會常常評估代碼質量,並不時地進行許多小調整,而不是一次修改許多東西。

在編寫代碼的時候,要常常留心能夠改進的微小方面。這可能會改善代碼的可讀性。也許你會發現能夠把一個方法拆成幾個更小的方法,使其變得更易於測試。在重構的原則指導下,能夠作出許多細微改善(《重構:改善既有代碼的設計》)。可使用測試優先開發方式,做爲強制進行增量式編程的方式。關鍵在於持續作一些細小而有用的事情,而不是作一段長時間的編程或重構。

這就是敏捷的方式。

具體技巧

  • 若是構建和測試循環話費的時間過長,你就不會但願常常運行它們了。要保證測試能夠快速運行。
  • 在編譯和測試運行中,停下來想想,並暫時遠離代碼細節,這是保證不會偏離正確方向的好辦法。
  • 要休息的話就要好好休息,請遠離鍵盤。
  • 要像重構你的代碼那樣,重構你的測試,並且要常常重構測試。

保持簡單

不要讓本身被迫進行過度設計,也不要將代碼過度複雜化。

Simple is not simplistic.
簡單這個詞彙被人們大大誤解了(在軟件開發工做以及人們的平常生活中,皆是如此)。它並不意味着簡陋、業餘或是能力不足。
優雅的代碼第一眼看上去,就知道它的用處,並且很簡潔。可是這樣的解決方案不是那麼容易想出來的。這就是說,優雅說易於理解和辨識的,可是要想建立出來就困可貴多了。

除非有不可辯駁的緣由,不然不要使用模式、原則和高難度技術之類的東西。

具體技巧

  • 代碼幾乎老是能夠獲得進一步精煉,可是到了某個點以後,再作改進就不會帶來任何實質性的好處了。這時開發人員就該停下來,去作其餘方面的工做了。
  • 要將目標牢記在心:簡單、可讀性高的代碼。強行讓代碼變得優雅與過早優化相似,一樣會產生惡劣的影響。
  • 固然,簡單的解決方案必需要知足功能需求。
  • 泰國簡潔不等於簡單,那樣沒法達到溝通的目的。
  • 一我的認爲簡單的東西,可能對另外一我的就意味着複雜。

編寫內聚的代碼

內聚性用來評估一個組件(包、模塊或配件)中成員的功能性。內聚程度高,代表各個成員共同完成了一個功能特性或是一組功能特性。內聚程度低的話,代表各個成員提供的功能是互不相干的。

如何組織一個組件中的代碼,會對開發人員的生產力和所有代碼的可維護性產生重要影響。在決定建立一個類的時候,問問本身,這個類的功能是否是與組件中其餘某個類的功能相似,並且功能緊密相關。這就是組件級的內聚性。

類也要遵循內聚性。若是一個類的方法和屬性共同完成了一個功能或是一系列緊密相關的功能,這個類就是內聚的。

一些設計技巧能夠起到幫助做用。舉例來講,咱們經常使用模型-視圖-控制器(MVC)模式來分離展現層邏輯、控制器和模型。這個模式很是有效,由於它可讓開發人員得到更高的內聚性。模型中的類包含一種功能,在控制器中的類包含另外的功能,而視圖中的類則只關心UI。

內聚性會影響一個組件的可重用性。

具體技巧

  • 有可能會把一些東西拆分紅不少微小的部分,而使其失去了實用價值。當你須要一隻襪子的時候,一盒棉線不能帶給你任何幫助。
  • 具備良好內聚性的代碼,可能會根據需求的變化,而成比例地進行變動。考慮一下,實現一個簡單的功能變化須要變動多少代碼。

告知,不要詢問

「面向過程的代碼取得信息,而後作出決策;面向對象的代碼讓別的對象去作事情。」

做爲某段代碼的調用者,開發人員絕對不該該基於被調用對象的狀態來作出任何決策,更不能去改變該對象的狀態。這樣的邏輯應該是被調用對象的責任,而不是你的。在該對象以外替它作決策,就違反了它的封裝原則,並且爲bug提供了滋生的土壤。

將命令與查詢分離開來
Keep commands separate from queries.
一個常規的命令可能會改變對象的狀態,並且有可能反饋一些有用的值,以方便使用。
一個查詢僅僅提供給開發人員對象的狀態,並不會對其外部的可見狀態進行修改。

像命令這種會產生內部影響的方法,強化了告知,不要詢問的建議。此外,保證查詢沒有反作用,也是很好的編碼實踐,由於開發人員能夠在單元測試中自由使用它們,在斷言或者調試器中調用它們,而不會改變應用的狀態。

具體技巧

  • 一個對象若是隻是用做大量數據容器,這樣的作法很可疑。有些狀況下會須要這樣的東西,但並不像想象的那麼頻繁。
  • 一個命令返回數據以方便使用時沒有問題的(若是須要的話,建立單獨讀取數據的方法也是能夠的)。
  • 絕對不容許一個看起來無辜的查詢去修改對象的狀態。

根據契約進行替換

保持系統靈活性的關鍵方式,是當新代碼取代原有代碼以後,其餘已有的代碼不會意識到任何差異。

Liskov替換原則告訴咱們:任何繼承後獲得的派生類對象,必須能夠替換任何被使用的基類對象,並且使用者沒必要知道任何差別。要遵照Liskov替換原則,相對基類的對應方法,派生類方法應該不要求更多,不承諾更少;要能夠進行自由的替換。在設計類的繼承層次時,這是一個很是重要的考慮因素。

若是違反了Liskov替換原則,繼承層次可能仍然能夠提供代碼的可重用性,可是將會失去可擴展性。類繼承關係的使用者如今必需要檢查給定對象的類型,以肯定如何針對其進行處理。當引入新的類以後,調用代碼必須常常評估並修正。這不是敏捷的方式。

針對is-a關係使用繼承;針對has-a或uses-a關係使用委託。
聚合時指在類中包含一個對象,而且該對象是其餘類的實例,開發人員將責任委託給所包含的對象來完成。

具體技巧

  • 相對繼承來講,委託更加靈活,適應力也更強。
  • 繼承不是魔鬼,只是長久以來被你們誤解了。
  • 若是你不肯定一個接口作出了什麼樣的承諾,或是有什麼樣的需求,那就很難提供一個對其有意義的實現了。
相關文章
相關標籤/搜索