Don’t Repeat Yourself,Repeat Yourself
Don't repeat yourself (DRY, or sometimes do not repeat yourself) 是一个旨在减少软件模式重复的软件开发原则,使用抽象或使用数据规范化来代替它以避免冗余。
平时工作中都会尽量避免写重复代码,但真的就不可以写重复的代码吗?
上代码
public class UserAuthenticator {
public void authenticate(String username, String password) {
if (!isValidUsername(username)) {
// ...throw InvalidUsernameException...
}
if (!isValidPassword(password)) {
// ...throw InvalidPasswordException...
}
//...省略其他代码...
}
private boolean isValidUsername(String username) {
// check not null, not empty
if (StringUtils.isBlank(username)) {
return false;
}
// check length: 4~64
int length = username.length();
if (length < 4 || length > 64) {
return false;
}
// contains only lowcase characters
if (!StringUtils.isAllLowerCase(username)) {
return false;
}
// contains only a~z,0~9,dot
for (int i = 0; i < length; ++i) {
char c = username.charAt(i);
if (!(c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.') {
return false;
}
}
return true;
}
private boolean isValidPassword(String password) {
// check not null, not empty
if (StringUtils.isBlank(password)) {
return false;
}
// check length: 4~64
int length = password.length();
if (length < 4 || length > 64) {
return false;
}
// contains only lowcase characters
if (!StringUtils.isAllLowerCase(password)) {
return false;
}
// contains only a~z,0~9,dot
for (int i = 0; i < length; ++i) {
char c = password.charAt(i);
if (!(c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.') {
return false;
}
}
return true;
}
}
代码中有两处明显重复的代码,isValidUserName() 函数和 isValidPassword() 函数,代码完全相同,为了移除重复我们重构一下上面代码
public class UserAuthenticatorV2 {
public void authenticate(String userName, String password) {
if (!isValidUsernameOrPassword(userName)) {
// ...throw InvalidUsernameException...
}
if (!isValidUsernameOrPassword(password)) {
// ...throw InvalidPasswordException...
}
}
private boolean isValidUsernameOrPassword(String usernameOrPassword) {
//省略实现逻辑
//跟原来的isValidUsername()或isValidPassword()的实现逻辑一样...
return true;
}
}
重构后去除了代码的重复,但是否意味着更好呢?合并之后的 isValidUserNameOrPassword() 函数,负责两件事情:验证用户名和验证密码,违反了“单一职责原则”和“接口隔离原则”。 isValidUserName() 和 isValidPassword() 虽然从实现逻辑上看起来是重复的,但是从语义上并不重复,一个是校验用户名,另一个是校验密码。尽管目前两个校验完全相同,如果未来修改其中一个校验逻辑,合并后的代码还得重新拆开。
之前工作中就遇到过这种问题,5个功能初始时完全相同,后来慢慢发生改变,开始时还尝试通过参数,状态来控制试图挣脱焦油坑,但最后不得不壮士断腕,重新拆开,瞬间神清气爽。
在程序设计中可以使用同样的方法做多件事,但应该杜绝同一件事由不同方法去做。
生活中亦是如此,避免重复,不惧重复,总结归纳,以终为始。
参考:设计模式之美