设计模式之美
王争
前 Google 工程师,《数据结构与算法之美》专栏作者
123425 人已学习
新⼈⾸单¥98
登录后,你可以任选6讲全文学习
课程目录
已完结/共 113 讲
设计模式与范式:行为型 (18讲)
设计模式之美
15
15
1.0x
00:00/00:00
登录|注册

21 | 理论七:重复的代码就一定违背DRY吗?如何提高代码的复用性?

移除重复执行的代码
重复执行的代码
代码修改导致问题
统一实现思路
两个功能相同的函数
抽象成更细粒度函数
语义不重复
重构示例
辩证思考和灵活应用
复用意识
应用模板等设计模式
继承、多态、抽象、封装
通用代码下沉
业务与非业务逻辑分离
模块化
满足单一职责原则
减少代码耦合
DRY原则
代码复用
代码执行重复
功能语义重复
实现逻辑重复
其他类型的代码重复
代码复用性
DRY原则
如何提高代码复用性?
什么是代码的复用性?
三种典型的代码重复情况
课堂讨论
重点回顾
代码复用性(Code Reusability)
DRY原则(Don't Repeat Yourself)
如何提高代码的复用性?

该思维导图由 AI 生成,仅供参考

在上一节课中,我们讲了 KISS 原则和 YAGNI 原则,KISS 原则可以说是人尽皆知。今天,我们再学习一个你肯定听过的原则,那就是 DRY 原则。它的英文描述为:Don’t Repeat Yourself。中文直译为:不要重复自己。将它应用在编程中,可以理解为:不要写重复的代码。
你可能会觉得,这条原则非常简单、非常容易应用。只要两段代码长得一样,那就是违反 DRY 原则了。真的是这样吗?答案是否定的。这是很多人对这条原则存在的误解。实际上,重复的代码不一定违反 DRY 原则,而且有些看似不重复的代码也有可能违反 DRY 原则。
听到这里,你可能会有很多疑问。没关系,今天我会结合具体的代码实例,来把这个问题讲清楚,纠正你对这个原则的错误认知。除此之外,DRY 原则与代码的复用性也有一些联系,所以,今天,我还会讲一讲,如何写出可复用性好的代码。
话不多说,让我们正式开始今天的学习吧!

DRY 原则(Don’t Repeat Yourself)

DRY 原则的定义非常简单,我就不再过度解读。今天,我们主要讲三种典型的代码重复情况,它们分别是:实现逻辑重复、功能语义重复和代码执行重复。这三种代码重复,有的看似违反 DRY,实际上并不违反;有的看似不违反,实际上却违反了。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了提高代码复用性的关键原则和方法。首先,通过具体代码示例,阐述了逻辑重复和功能语义重复对DRY原则的影响,并提出了解决方法。其次,强调了统一实现思路、抽象成更细粒度函数等方式来提高代码的可维护性和可读性。文章还介绍了代码复用性的重要性以及提高代码复用性的方法,包括减少代码耦合、满足单一职责原则、模块化、业务与非业务逻辑分离、通用代码下沉、继承、多态、抽象、封装、应用设计模式等。总之,本文通过深入的技术讨论,为读者提供了提高代码复用性的实用建议。同时,文章还强调了在没有具体复用需求时,不必过度投入开发成本,而是在未来遇到复用场景时进行重构,符合“Rule of Three”原则。文章内容丰富,重点突出,对于提高代码质量和可维护性具有重要指导意义。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《设计模式之美》
新⼈⾸单¥98
立即购买
登录 后留言

全部留言(107)

  • 最新
  • 精选
  • 花颜
    老师,我有个问题,在大型多人协作项目当中,类、功能都是分散给不同的人开发的,不同的开发者质量良莠不齐,而实现逻辑重复有代码重复率校验工具可以做检测,而功能语义重复和代码执行重复其实不是那么容易能够发现,即使通过有效的codeReview,有没有什么工具可以辅助我们查找功能语义重复和代码执行重复这两类重复,以及在大型团队项目下,如何应用这些原则呢?毕竟靠自觉总是很难的

    作者回复: 好像只能靠人本身 工具很难去断定是否符合某一设计原则

    2019-12-30
    2
    10
  • blacknhole
    1,提个小问题: “实现逻辑重复”一节的代码是不是有点问题啊? if (!(c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.') {}似乎应该改为if (!((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.')) {}。 2,说个小体会: “可复用性、可扩展性、可维护性……”与“复用性、扩展性、维护性……”,加不加“可”,其实没有本质差别。我以为,在通常的语境中(或者几乎任何情况下),两者都是可以通用的。 比如,可复用性高,说明能够复用,与当前是否已经复用无关。复用性高,是指当前已经大量复用,说明在这之前可复用性高。已经大量复用时,依然可以更多地复用,也即:复用性高,意味着可复用性依然高。 通常的语境中,也即通常提到“复用性”时,人们几乎只关注能不能复用,而不是已经复用了多少。所以,可以认为,可复用性高等同于复用性高。

    作者回复: 嗯嗯 确实有点问题 已经安排在改了

    2019-12-20
    3
    3
  • 黄林晴
    “Rule of Three”中的“Three”并不是真的就指确切的“三”,这里就是指“二”。😂 这句话看了好几遍

    作者回复: 😂

    2019-12-20
    8
    3
  • 🐝
    为了不重复,在合并代码里写if else 是否合适

    作者回复: 要权衡,没有最优解

    2020-11-23
  • 柴柴777
    我 之前就有个问题就说 我们如果用了组件化 每个模块算是单独的 尽管可能会写一个单独的util模块但是 还是存在着 重复代码,但是这些重复代码不在一个module里,那这样的到底算不算重复呢,,这些简单的部分的少量的重复不值得去单独加一个module

    作者回复: 有点重复是问题不大的,开发软件本身就没有绝对的对与错,也不是非黑即白,怎么合适怎么来,怎么舒服怎么来,不行就再重构。

    2020-01-06
  • 辣么大
    1、注释或者文档违反DRY 2、数据对象违反DRY 对于1,例如一个方法。写了好多的注释解释代码的执行逻辑,后续修改的这个方法的时候可能,忘记修改注释,造成对代码理解的困难。实际应用应该使用KISS原则,将方法写的见名知意,尽量容易阅读。注释不必过多。 对于2、例如类 class User String id Date registerDate int age int registedDays 其中 age可以由身份证号码算出来,而且每年都会递增。注册会员多少天了,也可以算出来。所以是不是可以考虑,数据只存储id和注册时间。其余两个字段可以算出来。 补充: DRY不是只代码重复,而是“知识”的重复,意思是指业务逻辑。例如由于沟通不足,两个程序员用两种不同的方法实现同样功能的校验。 DRY is about the duplication of knowledge, of intent. It’s about expressing the same thing in two different places, possibly in two totally different ways. 当代码的某些地方必须更改时,你是否发现自己在多个位置以多种不同格式进行了更改? 你是否需要更改代码和文档,或更改包含其的数据库架构和结构,或者…? 如果是这样,则您的代码不是DRY。 when some single facet of the code has to change, do you find yourself making that change in multiple places, and in multiple different formats? Do you have to change code and documentation, or a database schema and a structure that holds it, or…? If so, your code isn’t DRY. 参考: The Pragmatic Programmer: your journey to mastery, 20th Anniversary Edition (2nd Edition)
    2019-12-20
    20
    202
  • 岁月
    加油啊感觉更新太慢了一个下午就看完了..,一个星期至少更新10课吧.
    2019-12-20
    18
    29
  • 啦啦啦
    产品经理有时候设计产品功能的时候也会重复
    2019-12-20
    5
    26
  • magict4
    > 重复执行最明显的一个地方,就是在 login() 函数中,email 的校验逻辑被执行了两次。一次是在调用 checkIfUserExisted() 函数的时候,另一次是调用 getUserByEmail() 函数的时候。这个问题解决起来比较简单,我们只需要将校验逻辑从 UserRepo 中移除,统一放到 UserService 中就可以了。 这样处理会有一个问题:如果别的 xxxService 也需要用到 UserRepo,而且没有对 email 跟 password 进行校验,直接调用了 UserRepo.checkIfUserExisted() ,会产生异常。 一种方法是约定,所有关于 User 的操作都只能通过 UserService 进行,不能直接调用 UserRepo。 另一种方法是“强制” xxxService 进行校验。我们可以把 UserRepo.checkIfUserExisted 的方法签名改成 UserRepo.checkIfUserExisted(Email email, Password password) 并且把 validation 的逻辑封装在 Email 跟 Password 类的构造函数中。这样 xxxService 必须先把 email 跟 password 从 String 类型转成对应的 Email/Password 类,才能调用 UserRepo,validation 的逻辑会在转换中被强制执行。
    2019-12-20
    5
    19
  • AaronChun
    数据库转换对象beanDb和数据展现beanVo,从属性定义上来看可能存在大量重复,但从业务或系统分层来看,却是职责明确,功能单一的对象,所以这并不违反DRY原则。相反如果将两者共性部分抽离提取,后期倘若业务变更,修改就会牵扯到前台和后台,不符合单一职责和接口隔离原则。
    2019-12-24
    1
    16
收起评论
显示
设置
留言
99+
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部