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