编写代码中的 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 原则


版权声明:本文为surfirst原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。