前言
對于一個程式員來講如何來最直接的來衡量他的技術能力和産出呢?我想最直覺的作法是看他的代碼編寫能力,就拿我經常接觸的一些程式員來看,他們買了很多技術重構類書籍,但是看完後代碼編寫能力并沒有顯著提高。有人說可以用代碼review工具啊,但是像市面上的這些代碼review工具,隻能幫助我們解決表面的bug和規範點,還無法幫助我們發現更深層次的設計問題。
下面我将結合《軟體設計重構》這本書談談在進行代碼review的時候,需要關注的哪些點。
一、技術債務
何為技術債務?
技術債務是有意或無意的做出錯誤的或非最優的設計決策所引發的倆務
我們在代碼review的時候,經常碰到一些實作有瑕疵的方案,然後對方說因為時間太緊急臨時采用的方案,等第二期項目将其完善,于是往往第二期以後這個臨時方案就很難再去觸動了,時間越長代碼備援越大,越難去做修改,于是這就是典型的技術債務,債務越積越多,最後隻能重新徹底重構項目才能解決問題,這也叫做技術破産。
于是我們的做法有一個債務管理系統,在代碼review的時候,會将這些債務或者臨時方案錄入到系統中并制訂償還日期,那麼後續債務順利償還後,更改系統狀态,否遭遇一直沒有償還的,系統将以郵件的方式提醒,債務累積到一種數目後将與績效挂鈎考核。
二、設計的壞味道
前面隻是從債務的角度說明了所帶來的危害,其實引起技術債務的一個很重要的原因是對設計壞味和重構認識不足。
我們從設計的角度來看代碼時,要遵循六要素:
可了解性
代碼了解起來的難易程度
可修改性
在修改既有功能時,不會導緻連鎖反應。
可擴充性
支援新功能,不會導緻連鎖反應
可重用性
可以在代碼的其他地方引用其一塊代碼
可測試性
項目要能夠支援單元測試
可靠性
在正确的實作了功能的同時,也能夠考慮各種異常情況如何容錯
設計壞味的分類
抽象型壞味道
1、缺失抽象
舉例說明:
問題點:
在jdk1.0中方法printstacktrace()以字元串的方式将棧跟蹤列印到标準錯誤流:
public class throwabe { public void printstacktrace();
}
在需要以程式設計方式通路棧跟蹤元素的客戶程式中,必須要程式設計代碼來擷取資料,如行号等,由于客戶程度依賴這種字元串格式,jdk設計人員隻能在後續版本中相容這種格式了。
解決方法
public class throwabe { public void printstacktrace(); public stacktraceelement[] getstacktrace();
從jdk1.4起對java的api進行了改進,stacktraceelement類就是原來設計中缺失的對象,定義如下:
public final class stacktraceelement {
public string getfilename();
public int getlinenumber();
public string getclassname();
......
2、指令式抽象
舉例如下:
注:其中每個類都隻包括一個方法,這些方法分别是:create、display和copy等,是以存在指令式投象壞味,這種問題不僅會增加類的數量,還會增加開發和維護工作複雜性,而且将本應内聚的方法進行了不必要的分享。
解決方案
注:根據高内聚原則,統一歸集到一個report類中。
3、不完整的抽象
抽象未支援所有互補或相關的方法時,将導緻不完整的抽象,比如一個抽象的公有接口提供了用于配置設定資源的initalize()方法,但是卻沒有提供删除或者回收資源的方法dispose(),這種情況下就屬于不完整的抽象。
一些常見的互補方法對如下:
4、多方面的抽象
對象被賦予不止一項職責時,将導緻這種問題。
問題點
java.util.calendar類承擔了多項職責,不僅提供了日期相關的功能,還提供了與時間有關的功能,存大多方面抽象。由于同時支援日期和時間的方法,calendar類接口很大且難為了解,在jdk7中,java.util.calendar類包括了2825行代碼,有67個方法和71個字段。
對于calendar類,一種可能的重構是,将calendar類與時間相關的功能提取到新類time中,并将相關方法和字段移到新提取的類中,在java8中引入了一些支援日期和時間的新類,這些類位于java.time中。
5、不必要的抽象
public interface windowconstants {
public static final int do_nothing_on_close=0;
public static final int hide_on_close=1;
注:這個接口是典型的常量接口javax.swing.windowconstants,為啥用接口來存儲常量,因為首先枚舉是jdk1.5才引入的,其次通過接口中定義常量,可友善類通過繼承而不是委托來使用它們,因為通過實作接口,類可友善的通路接口中的常量,為什麼不使用類來存儲常量呢,因為接口支援多繼承。
那麼接口這樣定義常量有哪些問題呢?
a、派生類被無關的常量影響。
b、這些常量屬于實作細節,通過接口暴露它們違反封裝原則。
c、接口中存儲常量,修改它們會影響其他使用者。
将windowsconstants定義為枚舉,直接使用。
6、重複的抽象
根據dry原則規定:對于每個技術點,系統中都隻能有一個明确的表示。
導緻重複抽象的原因有:
a、複制-粘貼程式設計手法
b、即興維護
c、交流不暢
java.util.date和其派生類java.sql.date同名,這兩個類位于不同的包中,編譯器不會因為它們同名而報錯,但這讓使用者一頭霧水,這樣将導緻二義性。
将date名稱前面加上用途限定語,比如java.sql.sqldate更合适。
三、小結
由于内容太多,我們在第一部分隻介紹抽象型設計原則,接下來我将繼續寫模化型設計原則,封裝型設計原則和階層化設計原則,與大家深入讨論從設計角度來看,什麼樣的代碼才是真正的好代碼。
來源:中生代技術
<a href="https://mp.weixin.qq.com/s/p79vpon0j3s7lej9nskaxg" target="_blank">原文連結</a>