设计原则_DRY 原则
文章目录
- 1.DRY 原则
- 2.实现逻辑重复
- 3.功能语义重复
- 4.代码执行重复
- 5.注释重复
- 6.数据重复
- 7.提高代码复用性
1.DRY 原则
- 它的英文描述为:Don’t Repeat Yourself。中文直译为:不要重复自己。将它应用在编程中,可以理解为:不要写重复的代码。
- 很多人对这条原则存在的误解。实际上,重复的代码不一定违反 DRY 原则,而且
有些看似不重复的代码也有可能违反 DRY 原则
。 - DRY不是只代码重复,而是“知识”的重复,意思是指业务逻辑。例如由于沟通不足,两个程序员用两种不同的方法实现同样功能的校验。
2.实现逻辑重复
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 emptyif (StringUtils.isBlank(username)) {return false;}// check length: 4~64int length = username.length();if (length < 4 || length > 64) {return false;}// contains only lowcase charactersif (!StringUtils.isAllLowerCase(username)) {return false;}// contains only a~z,0~9,dotfor (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 emptyif (StringUtils.isBlank(password)) {return false;}// check length: 4~64int length = password.length();if (length < 4 || length > 64) {return false;}// contains only lowcase charactersif (!StringUtils.isAllLowerCase(password)) {return false;}// contains only a~z,0~9,dotfor (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;}
}
review:
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() 函数,负责两件事情:验证用户名和验证密码,违反了“单一职责原则”和“接口隔离原则”
- 实际上,即便将两个函数合并成 isValidUserNameOrPassword(),代码仍然存在问题。因为 isValidUserName() 和 isValidPassword() 两个函数,虽然从代码实现逻辑上看起来是重复的,但是从语义上并不重复。
- 从功能上来看,这两个函数干的是完全不重复的两件事情,一个是校验用户名,另一个是校验密码。尽管在目前的设计中,两个校验逻辑是完全一样的,但如果按照第二种写法,将两个函数的合并,那就会存在潜在的问题。在未来的某一天,如果我们修改了密码的校验逻辑,比如,允许密码包含大写字符,允许密码的长度为 8 到 64 个字符,那这个时候,isValidUserName() 和 isValidPassword() 的实现逻辑就会不相同。我们就要把合并后的函数,重新拆成合并前的那两个函数
3.功能语义重复
public boolean isValidIp(String ipAddress) {if (StringUtils.isBlank(ipAddress)) return false;String regex = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\."+ "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."+ "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."+ "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$";return ipAddress.matches(regex);
}public boolean checkIfIpValid(String ipAddress) {if (StringUtils.isBlank(ipAddress)) return false;String[] ipUnits = StringUtils.split(ipAddress, '.');if (ipUnits.length != 4) {return false;}for (int i = 0; i < 4; ++i) {int ipUnitIntValue;try {ipUnitIntValue = Integer.parseInt(ipUnits[i]);} catch (NumberFormatException e) {return false;}if (ipUnitIntValue < 0 || ipUnitIntValue > 255) {return false;}if (i == 0 && ipUnitIntValue == 0) {return false;}}return true;
}
- 尽管两段代码的实现逻辑不重复,但语义重复,也就是功能重复,我们认为它违反了 DRY 原则
- 假设我们不统一实现思路,那有些地方调用了 isValidIp() 函数,有些地方又调用了 checkIfIpValid() 函数,这就会导致代码看起来很奇怪,相当于给代码“埋坑”
- 如果哪天项目中 IP 地址是否合法的判定规则改变了,比如:255.255.255.255 不再被判定为合法的了,相应地,我们对 isValidIp() 的实现逻辑做了相应的修改,但却忘记了修改 checkIfIpValid() 函数。又或者,我们压根就不知道还存在一个功能相同的 checkIfIpValid() 函数,这样就会导致有些代码仍然使用老的 IP 地址判断逻辑,导致出现一些莫名其妙的 bug。
4.代码执行重复
public class UserService {private UserRepo userRepo;//通过依赖注入或者IOC框架注入public User login(String email, String password) {boolean existed = userRepo.checkIfUserExisted(email, password);if (!existed) {// ... throw AuthenticationFailureException...}User user = userRepo.getUserByEmail(email);return user;}
}public class UserRepo {public boolean checkIfUserExisted(String email, String password) {if (!EmailValidation.validate(email)) {// ... throw InvalidEmailException...}if (!PasswordValidation.validate(password)) {// ... throw InvalidPasswordException...}//...query db to check if email&password exists...}public User getUserByEmail(String email) {if (!EmailValidation.validate(email)) {// ... throw InvalidEmailException...}//...query db to get user by email...}
}
- 既没有逻辑重复,也没有语义重复,但仍然违反了 DRY 原则。这是因为代码中存在“执行重复”。
- 重复执行最明显的一个地方,就是在 login() 函数中,email 的校验逻辑被执行了两次。一次是在调用 checkIfUserExisted() 函数的时候,另一次是调用 getUserByEmail() 函数的时候。这个问题解决起来比较简单,我们只需要将校验逻辑从 UserRepo 中移除,统一放到 UserService 中就可以了
5.注释重复
例如一个方法。写了好多的注释解释代码的执行逻辑,后续修改的这个方法的时候可能,忘记修改注释,造成对代码理解的困难。实际应用应该使用KISS原则,将方法写的见名知意,尽量容易阅读。注释不必过多。
6.数据重复
class UserString idDate registerDateint ageint registedDays
其中 age可以由身份证号码算出来,而且每年都会递增。
注册会员多少天了,也可以算出来。
所以是不是可以考虑,数据只存储id和注册时间。其余两个字段可以算出来。
7.提高代码复用性
减少代码耦合
满足单一职责原则
模块化
业务与非业务逻辑分离
通用代码下沉
继承、多态、抽象、封装
应用模板等设计模式
设计原则_DRY 原则相关推荐
- 大型企业门户网站设计开发一般性原则和建议
[适用范围] 本文所述的原则.建议适用于大型企业信息门户网站的设计和开发,注意不是小型企业网站.一般企业电子商务网站.企业级Web应用系统. [一般性原则] 一.网站设计原则 第一原则:内容丰富.明确 ...
- 61条面向对象设计的经验原则
61条面向对象设计的经验原则 摘抄自<OOD 启思录>--Arthur J.Riel 著 鲍志云 译 "你不必严格遵守这些原则,违背它们也不会被处以宗教刑罚.但你应当把这些原则看 ...
- 深入理解面向对象设计的七大原则
一.面向对象设计的七大原则是什么? 1.开放封闭原则 2.里氏转换原则 3.依赖倒转原则 4.组合/聚合原则 5.接口隔离原则 6."迪米特"法则 7.单一职责原则 二.七大原则是 ...
- 代码设计的基础原则_设计原则:良好设计的基础
代码设计的基础原则 As designers, it's our goal to pass information in the most pleasing way possible. Startin ...
- 面象对象设计6大原则之六:迪米特原则
转载自 面象对象设计6大原则之六:迪米特原则 迪米特原则(LOD),The Law Of Demeter,也称为最少知识原则 定义 一个对象应该对其他对象有最少的了解. 也就是说一个类耦合和调用一个类 ...
- 面象对象设计6大原则之五:依赖倒置原则
转载自 面象对象设计6大原则之五:依赖倒置原则 依赖倒置原则(DIP),The Dependency Inversion Principle 定义 1.高层模块不应该依赖低层模块,两都应该依赖于抽象. ...
- 面象对象设计6大原则之四:接口隔离原则
转载自 面象对象设计6大原则之四:接口隔离原则 接口隔离原则(ISP),The Interface Segregation Principle 定义 客户端不需要强迫依赖那些它们不需要的接口. 类与接 ...
- 面象对象设计6大原则之三:里氏替换原则
转载自 面象对象设计6大原则之三:里氏替换原则 里氏替换原则(LSP),The Liskov Substitution Principle 定义 所有引用基类的地方必须能透明地引用其子类的对象,即子类 ...
- 面象对象设计6大原则之二:开放封闭原则
转载自 面象对象设计6大原则之二:开放封闭原则 开放封闭原则(OCP),The Open Closed Principle 定义 一个软件的实体,包括类.方法.模块.应该对扩展开放,对修改关闭. 也就 ...
最新文章
- Linux进阶之路————进程与服务管理
- 根据第xx天推算日期
- 【C语言】break,continue的区别
- mysql中如何将一个表中的部分记录合并,mysql - 如何从一个表中获取所有产品并从另一个包含多行的表中合并一行? - SO中文参考 - www.soinside.com...
- Unity3D启动时卡在Loading界面
- 协议森林1——小喇叭开始广播:以太网与WiFi
- centos老是自动更换ip地址解决方案
- Mybatis-9.28
- 月报总结|2月份Moonbeam最新进展
- burp抓包mumu模拟器
- mongodb-更新操作符
- 黑马程序员--Java学习01--java简介
- java-net-php-python-jspm高校固定资产管理系统计算机毕业设计程序
- STC8A单片机功能和应用电路
- STM32程序下载4:通过STM32CubePro-USB下载
- LAG()和LEAD() 分析函数详解
- 做外贸用哪个收费邮箱好,好用的邮箱品牌推荐
- 【ubuntu】linux中如何破解UltraEdit
- 信息系统项目管理师教程第3版教程 2017年9月出版
- 5G LTE窄带物联网(NB-IoT) 4