排在壞味道之首的是重復代碼。為什么它如此臭名昭著?各位看客可以看《消除壞味道》系列的開篇文章《重構,企業級應用的聖經》,本文不再重復說明,僅引用其中的觀點:
提示:對於任何事物,代碼中只應該出現一次,而且是唯一的一次。
下面我們將介紹如何消除幾種典型的重復代碼,以及如何培養消除重復代碼的習慣。
在同一個類中重復
第一種是在同一個類中存在重復代碼,它最容易識別,也最容易解決。看下面的例子:
try {
executorService.runTasks(...)
timer.scheduleAtFixedRate(
() => saveProgress(getProgress()),
period, period
)
} finally {
executorService.shutdown()
timer.cancel()
saveProgress(getProgress())
}
saveProgress(getProgress())
出現在兩個地方,可以通過 提取方法 來解決。
try {
executorService.runTasks(...)
timer.scheduleAtFixedRate(
() => persistProgress(),
period, period
)
} finally {
executorService.shutdown()
timer.cancel()
persistProgress()
}
def persistProgress() {
saveProgress(getProgress())
}
如果一個類超級長,上千行,那即使在一個類中也很難識別重復。這涉及到另一個壞味道“超長類”,《消滅壞味道》后續會有專門的文章來解決它。
在同一個類樹下重復
第二種是在同一個類樹下的不同子類中重復,比第一種識別起來困難些。
class Child1 extends Parent {
def run() {
init()
process()
}
...
}
class Child2 extends Parent {
def run() {
process()
}
...
}
可以通過 上移方法 和 模板方法 將公共部分上移到共同的父類。
class Parent {
def run() { // 模板方法
init()
process()
}
def init() {}
def process() //無函數體,等同於虛函數
}
class Child1 extends Parent {
override def init() { ... }
override def process() { ... }
}
class Child2 extends Parent {
override def process() { ... }
}
在不相干的類中重復
第三種是在兩個完全不相干的類中,如果不是專門地尋找很難發現。
class App1 {
val last3 = scores.sort().take(3)
}
class App2 {
val last10 = scores.sort().take(10)
}
可以先 提取方法 ,然后 移動方法 到新建的類,來消除重復。
class App1 {
val last3 = Seqs.lastN(scores, 3)
}
class App2 {
val last10 = Seqs.lastN(scores, 10)
}
object Seqs { // 新建的工具類
def lastN[E](seq: Seq[E], n: Int)
: Seq[E] = { // 返回值類型
seq.sort().take(n)
}
}
上面的例子,是在業務上沒有聯系的兩個類。
有可能是業務上有聯系,或者直接就是類似的,這個時候應該 抽取公共基類 ,然后按照第二種重復去重構。
細心的看官會發現,三種類型的重復有遞進關系,離的越來越遠,識別起來越來越難,解決起來也是。“在不相干的類中重復”可以通過重構轉換成前兩種,然后繼續重構得以解決。
要培養好的編碼習慣
解決重復並不困難,困難的是發現重復。發現重復並不困難,困難是培養發現重復的習慣。一碼在寫代碼完成功能前,會習慣性地左右前后看看,別人是否已經做過相關的功能,和我要做的是否可以共用代碼,這就很容易發現重復。
提示:如果你要動的是一個點,那么先了解她周圍的一圈。
不僅解決了重復,而且經過一段時間,我就發現對整個系統的理解程度,在不知不覺中提高了不少。
其實和做人是一樣的,著名的理財教練博多舍費爾在《小狗錢錢的爸爸 - 教你實現財務自由》一書中也強調:
不斷將私人領域變成主導領域,這是你對未來的責任。
私人領域是生活中與我相關的,主導領域是由我直接或間接影響的。碰到事情不能光是自掃門前雪,需要積極地參與,變被動為主動,這樣才會有更多的機會,讓我們越來越好。
總結
希望各位看官不只是學會了消除重復的技能,而且理解了文中的兩個重要提示。
- 對於任何事物,代碼中只應該出現一次,而且是唯一的一次。
- 如果你要動的是一個點,那么先了解她周圍的一圈。
關於示例代碼的說明
一碼覺得要講如何寫好代碼,就得拿代碼來說事。不過手機屏幕上代碼的展示是個大問題。為了保證示例代碼能讓大家讀得明白,一碼做了些取舍:
- 用片段,而不是完整可運行的代碼,突出重點
- 語言用Scala,因為她簡潔易讀
千萬不要覺得不懂Scala,有壓力,一碼會用她里面最平易近人的部分,放心吧。
提示:Scala是JVM平台上的一種語言,擁有簡潔和可擴展的特性,現在非常流行的大數據處理框架Spark就是用Scala開發的,學點Scala不會虧本的。
推薦
查看《大話重構》系列文章,請進入YoyaProgrammer公眾號,點擊 核心技術,點擊 大話重構。
分類 大話重構
優雅程序員 原創 轉載請注明出處