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 原則