文章目录

  • 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;}
}
  1. 合并之后的 isValidUserNameOrPassword() 函数,负责两件事情:验证用户名和验证密码,违反了“单一职责原则”和“接口隔离原则”
  2. 实际上,即便将两个函数合并成 isValidUserNameOrPassword(),代码仍然存在问题。因为 isValidUserName() 和 isValidPassword() 两个函数,虽然从代码实现逻辑上看起来是重复的,但是从语义上并不重复。
  3. 从功能上来看,这两个函数干的是完全不重复的两件事情,一个是校验用户名,另一个是校验密码。尽管在目前的设计中,两个校验逻辑是完全一样的,但如果按照第二种写法,将两个函数的合并,那就会存在潜在的问题。在未来的某一天,如果我们修改了密码的校验逻辑,比如,允许密码包含大写字符,允许密码的长度为 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;
}
  1. 尽管两段代码的实现逻辑不重复,但语义重复,也就是功能重复,我们认为它违反了 DRY 原则
  2. 假设我们不统一实现思路,那有些地方调用了 isValidIp() 函数,有些地方又调用了 checkIfIpValid() 函数,这就会导致代码看起来很奇怪,相当于给代码“埋坑”
  3. 如果哪天项目中 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...}
}
  1. 既没有逻辑重复,也没有语义重复,但仍然违反了 DRY 原则。这是因为代码中存在“执行重复”。
  2. 重复执行最明显的一个地方,就是在 login() 函数中,email 的校验逻辑被执行了两次。一次是在调用 checkIfUserExisted() 函数的时候,另一次是调用 getUserByEmail() 函数的时候。这个问题解决起来比较简单,我们只需要将校验逻辑从 UserRepo 中移除,统一放到 UserService 中就可以了

5.注释重复

例如一个方法。写了好多的注释解释代码的执行逻辑,后续修改的这个方法的时候可能,忘记修改注释,造成对代码理解的困难。实际应用应该使用KISS原则,将方法写的见名知意,尽量容易阅读。注释不必过多。

6.数据重复

class UserString idDate registerDateint ageint registedDays

其中 age可以由身份证号码算出来,而且每年都会递增。
注册会员多少天了,也可以算出来。
所以是不是可以考虑,数据只存储id和注册时间。其余两个字段可以算出来。

7.提高代码复用性

减少代码耦合
满足单一职责原则
模块化
业务与非业务逻辑分离
通用代码下沉
继承、多态、抽象、封装
应用模板等设计模式

设计原则_DRY 原则相关推荐

  1. 大型企业门户网站设计开发一般性原则和建议

    [适用范围] 本文所述的原则.建议适用于大型企业信息门户网站的设计和开发,注意不是小型企业网站.一般企业电子商务网站.企业级Web应用系统. [一般性原则] 一.网站设计原则 第一原则:内容丰富.明确 ...

  2. 61条面向对象设计的经验原则

    61条面向对象设计的经验原则 摘抄自<OOD 启思录>--Arthur J.Riel 著 鲍志云 译 "你不必严格遵守这些原则,违背它们也不会被处以宗教刑罚.但你应当把这些原则看 ...

  3. 深入理解面向对象设计的七大原则

    一.面向对象设计的七大原则是什么? 1.开放封闭原则 2.里氏转换原则 3.依赖倒转原则 4.组合/聚合原则 5.接口隔离原则 6."迪米特"法则 7.单一职责原则 二.七大原则是 ...

  4. 代码设计的基础原则_设计原则:良好设计的基础

    代码设计的基础原则 As designers, it's our goal to pass information in the most pleasing way possible. Startin ...

  5. 面象对象设计6大原则之六:迪米特原则

    转载自 面象对象设计6大原则之六:迪米特原则 迪米特原则(LOD),The Law Of Demeter,也称为最少知识原则 定义 一个对象应该对其他对象有最少的了解. 也就是说一个类耦合和调用一个类 ...

  6. 面象对象设计6大原则之五:依赖倒置原则

    转载自 面象对象设计6大原则之五:依赖倒置原则 依赖倒置原则(DIP),The Dependency Inversion Principle 定义 1.高层模块不应该依赖低层模块,两都应该依赖于抽象. ...

  7. 面象对象设计6大原则之四:接口隔离原则

    转载自 面象对象设计6大原则之四:接口隔离原则 接口隔离原则(ISP),The Interface Segregation Principle 定义 客户端不需要强迫依赖那些它们不需要的接口. 类与接 ...

  8. 面象对象设计6大原则之三:里氏替换原则

    转载自 面象对象设计6大原则之三:里氏替换原则 里氏替换原则(LSP),The Liskov Substitution Principle 定义 所有引用基类的地方必须能透明地引用其子类的对象,即子类 ...

  9. 面象对象设计6大原则之二:开放封闭原则

    转载自 面象对象设计6大原则之二:开放封闭原则 开放封闭原则(OCP),The Open Closed Principle 定义 一个软件的实体,包括类.方法.模块.应该对扩展开放,对修改关闭. 也就 ...

最新文章

  1. Linux进阶之路————进程与服务管理
  2. 根据第xx天推算日期
  3. 【C语言】break,continue的区别
  4. mysql中如何将一个表中的部分记录合并,mysql - 如何从一个表中获取所有产品并从另一个包含多行的表中合并一行? - SO中文参考 - www.soinside.com...
  5. Unity3D启动时卡在Loading界面
  6. 协议森林1——小喇叭开始广播:以太网与WiFi
  7. centos老是自动更换ip地址解决方案
  8. Mybatis-9.28
  9. 月报总结|2月份Moonbeam最新进展
  10. burp抓包mumu模拟器
  11. mongodb-更新操作符
  12. 黑马程序员--Java学习01--java简介
  13. java-net-php-python-jspm高校固定资产管理系统计算机毕业设计程序
  14. STC8A单片机功能和应用电路
  15. STM32程序下载4:通过STM32CubePro-USB下载
  16. LAG()和LEAD() 分析函数详解
  17. 做外贸用哪个收费邮箱好,好用的邮箱品牌推荐
  18. 【ubuntu】linux中如何破解UltraEdit
  19. 信息系统项目管理师教程第3版教程 2017年9月出版
  20. 5G LTE窄带物联网(NB-IoT) 4

热门文章

  1. 对博客园不敢恭维!!!
  2. Nginx不接受俄罗斯的贡献,违背开源协议和定义吗?我们冤枉F5了吗?
  3. Anaconda使用说明(个人版)
  4. UniFi USW-Flex 室内-室外 POE 交换机
  5. 从《网管员必读》系列丛书获奖看读者的真正需求——成绩回顾
  6. 基于ffmpeg的音视频转码、压制、录屏、裁切、合并、提取
  7. 无线远程上下载PLC程序
  8. 求方程的解(简化版)
  9. 阿里重磅开源在线分析诊断工具Arthas(阿尔萨斯)
  10. pycharm-社区版启动django项目的服务