天天看點

編寫代碼中的 DRY 原則

DRY 原則來自于《Pragmatic Programmer》即《務實的程式員》這本書。DRY 是英文 Don’t Repeat Yourself 的縮寫,從字面上了解就是不要重複你的代碼,但是作者其實說的是知識,更準确的定義是”每一份知識都必須在系統中擁有單一的,無歧義的,權威的代表“。

很抽象是不是,我們可以舉例來說明這個問題,假設我們的系統中有一個業務邏輯是付款 (payment),那麼付款業務的代碼應該隻能在你的系統中出現一次,也就是說在系統中隻能有一個付款子產品。我們關于如何付款,付款方式,付款的流程都由付款代碼負責。如果我們的系統由二十處地方要用到付款業務,我們絕不能把付款業務的代碼寫在二十多個地方,這不僅僅是為了修改友善,更重要的是在出現付款業務的問題的時候,我們能夠明确的找到問題所在的地方。

即使我們滿足了業務知識隻出現在單一子產品中,在單一的子產品中,我們也要求業務邏輯精煉地集中在一起。比如下面的代碼負責在汽車組裝之前檢查零部件是否齊全。

public boolean Validate(ArrayList<String> parts) throws Exception
{
	if(!parts.contains("wheel"))
		throw new Exception("This car doesn't have wheels.");
		
	if(!parts.contains("dashboard"))
		throw new Exception("This car doesn't have a dashboard.");
	
	if(!parts.contains("engine"))
		throw new Exception("This car doesn't have an engine.");
	...
	
	return true;
}
           

上面的代碼羅列所有汽車所需要的部件,如果缺少某個部件就抛出異常,它有兩個問題,一個是代碼重複,重複的 if 語句,重複的抛出異常,第二個是它把零部件這個知識分散在了代碼裡,增加和修改零部件很麻煩。比如,如果我們想要查找重複的零部件就必須檢視每行if, throw代碼,要修改零部件的名字,也得修改 if, throw代碼行。這樣的代碼顯然不 DRY。

下面的代碼把零部件集中地放在了一起,擠幹了上面的代碼的水分。

private String[] requiredParts = new String[] { "wheel", "dashboard", "engine", ... };

public boolean Validate(ArrayList<String> parts) throws Exception 
{
	ArrayList<String> required = new ArrayList<String>(Arrays.asList(requiredParts));
	
	boolean validated = parts.containsAll(required);
	
	if (!validated) {
	    var missing = parts.stream().filter(p -> !required.contains(p)).findFirst();
	    throw new Exception(String.format("This car doesn't have %s", missing.get()));
	}
	
	return true;
}
           

總結

代碼原則中的 DRY 原則,要求程式員擠幹代碼的水分,每一份知識都在系統中隻有單一的,無歧義的,權威的代表,方法 (method) 不要過長,分割業務邏輯,盡量重用我們的代碼片段。

參考

下面是我寫的代碼編寫原則的其他文章:

軟體設計原則之 SOLID Principle

單元測試中的 AAA 規則

單元測試中的 FIRST 原則