前言
對(duì)于一個(gè)程序員來講如何來最直接的來衡量他的技術(shù)能力和產(chǎn)出呢?我想最直觀的作法是看他的代碼編寫能力评架,就拿我經(jīng)常接觸的一些程序員來看,他們買了很多技術(shù)重構(gòu)類書籍,但是看完后代碼編寫能力并沒有顯著提高夭坪。有人說可以用代碼review工具啊,但是像市面上的這些代碼review工具过椎,只能幫助我們解決表面的bug和規(guī)范點(diǎn)室梅,還無法幫助我們發(fā)現(xiàn)更深層次的設(shè)計(jì)問題。
下面我將結(jié)合《軟件設(shè)計(jì)重構(gòu)》這本書談?wù)勗谶M(jìn)行代碼review的時(shí)候,需要關(guān)注的哪些點(diǎn)亡鼠。
一赏殃、技術(shù)債務(wù)
何為技術(shù)債務(wù)?
技術(shù)債務(wù)是有意或無意的做出錯(cuò)誤的或非最優(yōu)的設(shè)計(jì)決策所引發(fā)的倆務(wù)
我們?cè)诖areview的時(shí)候,經(jīng)常碰到一些實(shí)現(xiàn)有瑕疵的方案间涵,然后對(duì)方說因?yàn)闀r(shí)間太緊急臨時(shí)采用的方案仁热,等第二期項(xiàng)目將其完善,于是往往第二期以后這個(gè)臨時(shí)方案就很難再去觸動(dòng)了勾哩,時(shí)間越長(zhǎng)代碼冗余越大抗蠢,越難去做修改,于是這就是典型的技術(shù)債務(wù)思劳,債務(wù)越積越多迅矛,最后只能重新徹底重構(gòu)項(xiàng)目才能解決問題,這也叫做技術(shù)破產(chǎn)潜叛。
于是我們的做法有一個(gè)債務(wù)管理系統(tǒng)诬乞,在代碼review的時(shí)候,會(huì)將這些債務(wù)或者臨時(shí)方案錄入到系統(tǒng)中并制訂償還日期钠导,那么后續(xù)債務(wù)順利償還后震嫉,更改系統(tǒng)狀態(tài),否遭遇一直沒有償還的牡属,系統(tǒng)將以郵件的方式提醒票堵,債務(wù)累積到一種數(shù)目后將與績(jī)效掛鉤考核。
二逮栅、設(shè)計(jì)的壞味道
前面只是從債務(wù)的角度說明了所帶來的危害悴势,其實(shí)引起技術(shù)債務(wù)的一個(gè)很重要的原因是對(duì)設(shè)計(jì)壞味和重構(gòu)認(rèn)識(shí)不足。
我們從設(shè)計(jì)的角度來看代碼時(shí)措伐,要遵循六要素:
- 可理解性
代碼理解起來的難易程度 - 可修改性
在修改既有功能時(shí)特纤,不會(huì)導(dǎo)致連鎖反應(yīng)。 - 可擴(kuò)展性
支持新功能侥加,不會(huì)導(dǎo)致連鎖反應(yīng) - 可重用性
可以在代碼的其他地方引用其一塊代碼 - 可測(cè)試性
項(xiàng)目要能夠支持單元測(cè)試 - 可靠性
在正確的實(shí)現(xiàn)了功能的同時(shí)捧存,也能夠考慮各種異常情況如何容錯(cuò)
2.1、設(shè)計(jì)壞味的分類
2.1.1 抽象型壞味道
1担败、缺失抽象
舉例說明:
- 問題點(diǎn):
在JDK1.0中方法printStackTrace()
以字符串的方式將棧跟蹤打印到標(biāo)準(zhǔn)錯(cuò)誤流:
public class Throwabe {
public void printStackTrace();
}
在需要以編程方式訪問棧跟蹤元素的客戶程序中昔穴,必須要編程代碼來獲取數(shù)據(jù),如行號(hào)等提前,由于客戶程度依賴這種字符串格式吗货,JDK設(shè)計(jì)人員只能在后續(xù)版本中兼容這種格式了。
- 解決方法
public class Throwabe {
public void printStackTrace();
public StackTraceElement[] getStackTrace();
}
從Jdk1.4起對(duì)JAVA的API進(jìn)行了改進(jìn)狈网,StackTraceElement類就是原來設(shè)計(jì)中缺失的對(duì)象宙搬,定義如下:
public final class StackTraceElement {
public String getFilename();
public int getLineNumber();
public String getClassname();
......
}
2笨腥、命令式抽象
舉例如下:
-
問題點(diǎn):
Paste_Image.png
注:其中每個(gè)類都只包括一個(gè)方法,這些方法分別是:create勇垛、display和copy等扇雕,因此存在命令式投象壞味,這種問題不僅會(huì)增加類的數(shù)量窥摄,還會(huì)增加開發(fā)和維護(hù)工作復(fù)雜性镶奉,而且將本應(yīng)內(nèi)聚的方法進(jìn)行了不必要的分享。
-
解決方案
Paste_Image.png
注:根據(jù)高內(nèi)聚原則崭放,統(tǒng)一歸集到一個(gè)Report類中哨苛。
3、不完整的抽象
抽象未支持所有互補(bǔ)或相關(guān)的方法時(shí)币砂,將導(dǎo)致不完整的抽象建峭,比如一個(gè)抽象的公有接口提供了用于分配資源的initalize()方法,但是卻沒有提供刪除或者回收資源的方法dispose()决摧,這種情況下就屬于不完整的抽象亿蒸。
一些常見的互補(bǔ)方法對(duì)如下:
列一 | 列二 | 列三 | 列四 |
---|---|---|---|
min/max | open/close | create/destroy | get/set |
start/stop | print/scan | first/last | begin/end |
source/target | lock/unlock | show/hide | up/down |
enable/disable | acquire/release | left/right | on/off |
4、多方面的抽象
對(duì)象被賦予不止一項(xiàng)職責(zé)時(shí)掌桩,將導(dǎo)致這種問題边锁。
舉例如下:
問題點(diǎn)
java.util.Calendar類承擔(dān)了多項(xiàng)職責(zé),不僅提供了日期相關(guān)的功能波岛,還提供了與時(shí)間有關(guān)的功能茅坛,存大多方面抽象。由于同時(shí)支持日期和時(shí)間的方法则拷,Calendar類接口很大且難為理解贡蓖,在JDK7中,java.util.Calendar類包括了2825行代碼煌茬,有67個(gè)方法和71個(gè)字段斥铺。解決方案
對(duì)于Calendar類,一種可能的重構(gòu)是坛善,將Calendar類與時(shí)間相關(guān)的功能提取到新類Time中晾蜘,并將相關(guān)方法和字段移到新提取的類中,在Java8中引入了一些支持日期和時(shí)間的新類浑吟,這些類位于java.time中笙纤。
5、不必要的抽象
舉例如下:
- 問題點(diǎn):
public interface WindowConstants {
public static final int DO_NOTHING_ON_CLOSE=0;
public static final int HIDE_ON_CLOSE=1;
}
注:這個(gè)接口是典型的常量接口javax.swing.WindowConstants组力,為啥用接口來存儲(chǔ)常量,因?yàn)槭紫让杜e是jdk1.5才引入的抖拴,其次通過接口中定義常量燎字,可方便類通過繼承而不是委托來使用它們腥椒,因?yàn)橥ㄟ^實(shí)現(xiàn)接口,類可方便的訪問接口中的常量候衍,為什么不使用類來存儲(chǔ)常量呢笼蛛,因?yàn)榻涌谥С侄嗬^承。
那么接口這樣定義常量有哪些問題呢蛉鹿?
A滨砍、派生類被無關(guān)的常量影響。
B妖异、這些常量屬于實(shí)現(xiàn)細(xì)節(jié)惋戏,通過接口暴露它們違反封裝原則。
C他膳、接口中存儲(chǔ)常量响逢,修改它們會(huì)影響其他使用者。
- 解決方案
將WindowsConstants定義為枚舉棕孙,直接使用舔亭。
6、重復(fù)的抽象
根據(jù)DRY原則規(guī)定:對(duì)于每個(gè)技術(shù)點(diǎn)蟀俊,系統(tǒng)中都只能有一個(gè)明確的表示钦铺。
導(dǎo)致重復(fù)抽象的原因有:
A、復(fù)制-粘貼編程手法
B肢预、即興維護(hù)
C职抡、交流不暢
舉例說明:
問題點(diǎn):
java.util.Date和其派生類java.sql.Date同名,這兩個(gè)類位于不同的包中误甚,編譯器不會(huì)因?yàn)樗鼈兺鴪?bào)錯(cuò)缚甩,但這讓使用者一頭霧水,這樣將導(dǎo)致二義性窑邦。解決方案
將Date名稱前面加上用途限定語(yǔ)擅威,比如java.sql.SQLDate更合適。
三冈钦、小結(jié)
由于內(nèi)容太多郊丛,我們?cè)诘谝徊糠种唤榻B抽象型設(shè)計(jì)原則,接下來我將繼續(xù)寫那粕福化型設(shè)計(jì)原則厉熟,封裝型設(shè)計(jì)原則和層次化設(shè)計(jì)原則,與大家深入討論從設(shè)計(jì)角度來看较幌,什么樣的代碼才是真正的好代碼揍瑟。