大話重構 之 解決萬惡之首“重復代碼”


排在壞味道之首的是重復代碼。為什么它如此臭名昭著?各位看客可以看《消除壞味道》系列的開篇文章《重構,企業級應用的聖經》,本文不再重復說明,僅引用其中的觀點:

提示:對於任何事物,代碼中只應該出現一次,而且是唯一的一次。

下面我們將介紹如何消除幾種典型的重復代碼,以及如何培養消除重復代碼的習慣。

在同一個類中重復

第一種是在同一個類中存在重復代碼,它最容易識別,也最容易解決。看下面的例子:

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)
	}
}

上面的例子,是在業務上沒有聯系的兩個類。

有可能是業務上有聯系,或者直接就是類似的,這個時候應該 抽取公共基類 ,然后按照第二種重復去重構。

細心的看官會發現,三種類型的重復有遞進關系,離的越來越遠,識別起來越來越難,解決起來也是。“在不相干的類中重復”可以通過重構轉換成前兩種,然后繼續重構得以解決。

要培養好的編碼習慣

good habit

解決重復並不困難,困難的是發現重復。發現重復並不困難,困難是培養發現重復的習慣。一碼在寫代碼完成功能前,會習慣性地左右前后看看,別人是否已經做過相關的功能,和我要做的是否可以共用代碼,這就很容易發現重復。

提示:如果你要動的是一個點,那么先了解她周圍的一圈。

不僅解決了重復,而且經過一段時間,我就發現對整個系統的理解程度,在不知不覺中提高了不少。

其實和做人是一樣的,著名的理財教練博多舍費爾在《小狗錢錢的爸爸 - 教你實現財務自由》一書中也強調:

不斷將私人領域變成主導領域,這是你對未來的責任。

私人領域是生活中與我相關的,主導領域是由我直接或間接影響的。碰到事情不能光是自掃門前雪,需要積極地參與,變被動為主動,這樣才會有更多的機會,讓我們越來越好。

總結

希望各位看官不只是學會了消除重復的技能,而且理解了文中的兩個重要提示。

  1. 對於任何事物,代碼中只應該出現一次,而且是唯一的一次。
  2. 如果你要動的是一個點,那么先了解她周圍的一圈。

關於示例代碼的說明

一碼覺得要講如何寫好代碼,就得拿代碼來說事。不過手機屏幕上代碼的展示是個大問題。為了保證示例代碼能讓大家讀得明白,一碼做了些取舍:

  1. 用片段,而不是完整可運行的代碼,突出重點
  2. 語言用Scala,因為她簡潔易讀

千萬不要覺得不懂Scala,有壓力,一碼會用她里面最平易近人的部分,放心吧。

提示:Scala是JVM平台上的一種語言,擁有簡潔和可擴展的特性,現在非常流行的大數據處理框架Spark就是用Scala開發的,學點Scala不會虧本的。

推薦

解決萬惡之首“重復代碼”

消除過長方法

消除巨無霸類

答讀者問

你的參數列表像蚯蚓一樣讓人厭惡嗎

職責單一原則真的簡單嗎

查看《大話重構》系列文章,請進入YoyaProgrammer公眾號,點擊 核心技術,點擊 大話重構。

分類 大話重構

優雅程序員 原創 轉載請注明出處

圖片二維碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM