文章目录

  • KISS 原则
  • YAGNI 原则
  • DRY 原则
  • 代码复用性(Code Reusability)
  • 何为“高内聚、松耦合”?
  • 迪米特法则(LOD)
  • 为什么要分 MVC 三层开发?
  • BO、VO、Entity 存在的意义是什么?

KISS 原则

  • ISS 原则的英文描述:Keep It Simple and Stupid。翻译成中文就是:尽量保持简单。
  • KISS 原则是保持代码可读和可维护的重要手段。KISS 原则中的“简单”并不是以代码行数来考量的。代码行数越少并不代表代码越简单,我们还要考虑逻辑复杂度、实现难度、代码的可读性等。而且,本身就复杂的问题,用复杂的方法解决,并不违背 KISS 原则。除此之外,同样的代码,在某个业务场景下满足 KISS 原则,换一个应用场景可能就不满足了。

如何写出满足 KISS 原则的代码?

  • 不要使用同事可能不懂的技术来实现代码。比如前面例子中的正则表达式,还有一些编程语言中过于高级的语法等。
  • 不要重复造轮子,要善于使用已经有的工具类库。经验证明,自己去实现这些类库,出 bug 的概率会更高,维护的成本也比较高。
  • 不要过度优化。不要过度使用一些奇技淫巧(比如,位运算代替算术运算、复杂的条件语句代替 if-else、使用一些过于底层的函数等)来优化代码,牺牲代码的可读性。

YAGNI 原则

  • YAGNI 原则的英文全称是:You Ain’t Gonna Need It。直译就是:你不会需要它。
  • 当用在软件开发中的时候,它的意思是:不要去设计当前用不到的功能;不要去编写当前用不到的代码。实际上,这条原则的核心思想就是:不要做过度设计。

YAGNI 原则跟 KISS 原则并非一回事儿。KISS 原则讲的是“如何做”的问题(尽量保持简单),而 YAGNI 原则说的是“要不要做”的问题(当前不需要的就不要做)。

DRY 原则

  • DRY 原则的英文描述为:Don’t Repeat Yourself。中文直译为:不要重复自己。将它应用在编程中,可以理解为:不要写重复的代码。
  • 三种代码重复的情况:实现逻辑重复、功能语义重复、代码执行重复。
    • 实现逻辑重复,但功能语义不重复的代码,并不违反 DRY 原则。
    • 实现逻辑不重复,但功能语义重复的代码,也算是违反 DRY 原则。
    • 代码执行重复也算是违反 DRY 原则。

代码复用性(Code Reusability)

  • 首先来区分三个概念:代码复用性(Code Reusability)、代码复用(Code Resue)和 DRY 原则。

    • 代码复用表示一种行为:我们在开发新功能的时候,尽量复用已经存在的代码。
    • 代码的可复用性表示一段代码可被复用的特性或能力:我们在编写代码的时候,让代码尽量可复用。
    • DRY 原则是一条原则:不要写重复的代码。
  • 从定义描述上,它们好像有点类似,但深究起来,三者的区别还是蛮大的。
    • 首先,“不重复”并不代表“可复用”。在一个项目代码中,可能不存在任何重复的代码,但也并不表示里面有可复用的代码,不重复和可复用完全是两个概念。
    • 其次,“复用”和“可复用性”关注角度不同。代码“可复用性”是从代码开发者的角度来讲的,“复用”是从代码使用者的角度来讲的。
  • 尽管复用、可复用性、DRY 原则这三者从理解上有所区别,但实际上要达到的目的都是类似的,都是为了减少代码量,提高代码的可读性、可维护性。除此之外,复用已经经过测试的老代码,bug 会比从零重新开发要少。

怎么提高代码复用性?

  • 减少代码耦合

    • 对于高度耦合的代码,当我们希望复用其中的一个功能,想把这个功能的代码抽取出来成为一个独立的模块、类或者函数的时候,往往会发现牵一发而动全身。移动一点代码,就要牵连到很多其他相关的代码。所以,高度耦合的代码会影响到代码的复用性,我们要尽量减少代码耦合。
  • 满足单一职责原则
    • 如果职责不够单一,模块、类设计得大而全,那依赖它的代码或者它依赖的代码就会比较多,进而增加了代码的耦合。根据上一点,也就会影响到代码的复用性。相反,越细粒度的代码,代码的通用性会越好,越容易被复用。
  • 模块化
    • 这里的“模块”,不单单指一组类构成的模块,还可以理解为单个类、函数。我们要善于将功能独立的代码,封装成模块。独立的模块就像一块一块的积木,更加容易复用,可以直接拿来搭建更加复杂的系统。
  • 业务与非业务逻辑分离
    • 越是跟业务无关的代码越是容易复用,越是针对特定业务的代码越难复用。所以,为了复用跟业务无关的代码,我们将业务和非业务逻辑代码分离,抽取成一些通用的框架、类库、组件等。
  • 通用代码下沉
    • 从分层的角度来看,越底层的代码越通用、会被越多的模块调用,越应该设计得足够可复用。一般情况下,在代码分层之后,为了避免交叉调用导致调用关系混乱,我们只允许上层代码调用下层代码及同层代码之间的调用,杜绝下层代码调用上层代码。所以,通用的代码我们尽量下沉到更下层。
  • 继承、多态、抽象、封装
    • 在讲面向对象特性的时候,我们讲到,利用继承,可以将公共的代码抽取到父类,子类复用父类的属性和方法。利用多态,我们可以动态地替换一段代码的部分逻辑,让这段代码可复用。除此之外,抽象和封装,从更加广义的层面、而非狭义的面向对象特性的层面来理解的话,越抽象、越不依赖具体的实现,越容易复用。代码封装成模块,隐藏可变的细节、暴露不变的接口,就越容易复用。
  • 应用模板等设计模式
    • 一些设计模式,也能提高代码的复用性。比如,模板模式利用了多态来实现,可以灵活地替换其中的部分代码,整个流程模板代码可复用。关于应用设计模式提高代码复用性这一部分,我们留在后面慢慢来讲解。

何为“高内聚、松耦合”?

  • “高内聚、松耦合”是一个非常重要的设计思想,能够有效地提高代码的可读性和可维护性,缩小功能改动导致的代码改动范围。很多设计原则都以实现代码的“高内聚、松耦合”为目的,比如单一职责原则、基于接口而非实现编程等。
  • 高内聚、松耦合”是一个比较通用的设计思想,可以用来指导不同粒度代码的设计与开发,比如系统、模块、类,甚至是函数,也可以应用到不同的开发场景中,比如微服务、框架、组件、类库等。
  • 以“类”作为这个设计思想的应用对象来展开讲解。在这个设计思想中,“高内聚”用来指导类本身的设计,“松耦合”用来指导类与类之间依赖关系的设计。不过,这两者并非完全独立不相干。高内聚有助于松耦合,松耦合又需要高内聚的支持。

什么是“高内聚”呢?

  • 所谓高内聚,就是指相近的功能应该放到同一个类中,不相近的功能不要放到同一个类中。相近的功能往往会被同时修改,放到同一个类中,修改会比较集中,代码容易维护。实际上,我们前面讲过的单一职责原则是实现代码高内聚非常有效的设计原则。

什么是“松耦合”?

  • 所谓松耦合是说,在代码中,类与类之间的依赖关系简单清晰。即使两个类有依赖关系,一个类的代码改动不会或者很少导致依赖类的代码改动。实际上,依赖注入、接口隔离、基于接口而非实现编程,以及迪米特法则,都是为了实现代码的松耦合。

“内聚”和“耦合”之间的关系

  • “高内聚”有助于“松耦合”,同理,“低内聚”也会导致“紧耦合”。
  • 图中左边部分的代码结构是“高内聚、松耦合”;右边部分正好相反,是“低内聚、紧耦合”。
  • 图中左边部分的代码设计中,类的粒度比较小,每个类的职责都比较单一。相近的功能都放到了一个类中,不相近的功能被分割到了多个类中。这样类更加独立,代码的内聚性更好。因为职责单一,所以每个类被依赖的类就会比较少,代码低耦合。一个类的修改,只会影响到一个依赖类的代码改动。我们只需要测试这一个依赖类是否还能正常工作就行了。
  • 图中右边部分的代码设计中,类粒度比较大,低内聚,功能大而全,不相近的功能放到了一个类中。这就导致很多其他类都依赖这个类。当我们修改这个类的某一个功能代码的时候,会影响依赖它的多个类。我们需要测试这三个依赖类,是否还能正常工作。这也就是所谓的“牵一发而动全身”。
  • 从图中我们也可以看出,高内聚、低耦合的代码结构更加简单、清晰,相应地,在可维护性和可读性上确实要好很多。

迪米特法则(LOD)

  • 迪米特法则的英文翻译是:Law of Demeter,缩写是 LOD。它还有另外一个更加达意的名字,叫作最小知识原则,英文翻译为:The Least Knowledge Principle。关于这个设计原则,英文定义:Each unit should have only limited knowledge about other units: only units “closely” related to the current unit. Or: Each unit should only talk to its friends; Don’t talk to strangers.直译成中文,就是下面这个样子:
    每个模块(unit)只应该了解那些与它关系密切的模块(units: only units “closely” related to the current unit)的有限知识(knowledge)。或者说,每个模块只和自己的朋友“说话”(talk),不和陌生人“说话”(talk)。

如何理解“迪米特法则”?

  • 不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口。迪米特法则是希望减少类之间的耦合,让类越独立越好。每个类都应该少了解系统的其他部分。一旦发生变化,需要了解这一变化的类就会比较少。

常见的系统之间的交互方式有两种,一种是同步接口调用,另一种是利用消息中间件异步调用。第一种方式简单直接,第二种方式的解耦效果更好。上下层系统之间的调用倾向于通过同步接口,同层之间的调用倾向于异步消息调用。

为什么要分 MVC 三层开发?

  • 分层能起到代码复用的作用

    • 同一个 Repository 可能会被多个 Service 来调用,同一个 Service 可能会被多个 Controller 调用。
  • 分层能起到隔离变化的作用
    • 分层体现了一种抽象和封装的设计思想。
  • 分层能起到隔离关注点的作用
    • Repository 层只关注数据的读写。Service 层只关注业务逻辑,不关注数据的来源。Controller 层只关注与外界打交道,数据校验、封装、格式转换,并不关心业务逻辑。三层之间的关注点不同,分层之后,职责分明,更加符合单一职责原则,代码的内聚性更好。
  • 分层能提高代码的可测试性
    • 单元测试不依赖不可控的外部组件,比如数据库。分层之后,Repsitory 层的代码通过依赖注入的方式供 Service 层使用,当要测试包含核心业务逻辑的 Service 层代码的时候,我们可以用 mock 的数据源替代真实的数据库,注入到 Service 层代码中。
  • 分层能应对系统的复杂性
    • 当一个类或一个函数的代码过多之后,可读性、可维护性就会变差。那就要想办法拆分。拆分有垂直和水平两个方向。水平方向基于业务来做拆分,就是模块化;垂直方向基于流程来做拆分,就是这里说的分层。

BO、VO、Entity 存在的意义是什么?

  • 针对 Controller、Service、Repository 三层,每层都会定义相应的数据对象,它们分别是 VO(View Object)、BO(Business Object)、Entity,例如 UserVo、UserBo、UserEntity。在实际的开发中,VO、BO、Entity 可能存在大量的重复字段,甚至三者包含的字段完全一样。在开发的过程中,我们经常需要重复定义三个几乎一样的类,显然是一种重复劳动。

相对于每层定义各自的数据对象来说,是不是定义一个公共的数据对象更好些呢?

  • 实际上,更加推荐每层都定义各自的数据对象这种设计思路,主要有以下 3 个方面的原因。

    • VO、BO、Entity 并非完全一样。比如,我们可以在 UserEntity、UserBo 中定义 Password 字段,但显然不能在 UserVo 中定义 Password 字段,否则就会将用户的密码暴露出去。
    • VO、BO、Entity 三个类虽然代码重复,但功能语义不重复,从职责上讲是不一样的。所以,也并不能算违背 DRY 原则。针对这种情况,如果合并为同一个类,那也会存在后期因为需求的变化而需要再拆分的问题。
    • 为了尽量减少每层之间的耦合,把职责边界划分明确,每层都会维护自己的数据对象,层与层之间通过接口交互。数据从下一层传递到上一层的时候,将下一层的数据对象转化成上一层的数据对象,再继续处理。虽然这样的设计稍微有些繁琐,每层都需要定义各自的数据对象,需要做数据对象之间的转化,但是分层清晰。对于非常大的项目来说,结构清晰是第一位的!
  • 从设计的角度来说,VO、BO、Entity 的设计思路并不违反 DRY 原则,为了分层清晰、减少耦合,多维护几个类的成本也并不是不能接受的。但是,如果你真的有代码洁癖,对于代码重复的问题,我们可以通过继承或者组合来解决。
  • 如何进行数据对象之间的转化? 最简单的方式就是手动复制。当然,你也可以使用 Java 中提供了数据对象转化工具,比如 BeanUtils、Dozer 等,可以大大简化繁琐的对象转化工作。
  • 尽管 VO、BO、Entity 的设计违背 OOP 的封装特性,有被随意修改的风险。但 Entity 和 VO 的生命周期是有限的,都仅限在本层范围内,相对来说是安全的。Service 层包含比较多的业务逻辑代码,所以 BO 就存在被任意修改的风险了。为了使用方便,我们只能做一些妥协,放弃 BO 的封装特性,由程序员自己来负责这些数据对象的不被错误使用。

你知道的越多,你不知道的越多。

设计模式之设计原则与思想:设计原则(二)相关推荐

  1. 设计原则与思想:设计原则

    这里写目录标题 理论一:对于单一职责原则,如何判断某个类的职责是否够单一 理论二:如何做到"对扩展开放,修改关闭?扩展和修改各指什么?" 理论三:里式替换(LSP)跟多态有何区别? ...

  2. Java-单机版的书店管理系统(练习设计模块和思想_系列 六 )

    本系列前面博客的链接: Java-单机版的书店管理系统(练习设计模块和思想_系列 五 ) http://blog.csdn.net/qq_26525215/article/details/511368 ...

  3. Java-单机版的书店管理系统(练习设计模块和思想_系列 四(2) )

    Java-单机版的书店管理系统(练习设计模块和思想_系列 四(1) ): http://blog.csdn.net/qq_26525215/article/details/51116429 Java- ...

  4. 《终极海报——23位创意大咖的设计评论与思想》目录—导读

    致 终极海报--23位创意大咖的设计评论与思想 致我的侄子卡伊 他帮助我以如此多新奇的视角 去观察这个世界 序 终极海报--23位创意大咖的设计评论与思想 设计师们都在想什么?呃,我们永远也不会知道. ...

  5. 【设计模式之美 设计原则与思想:设计原则】22 | 理论八:如何用迪米特法则(LOD)实现“高内聚、松耦合”?

    今天,我们讲最后一个设计原则:迪米特法则.尽管它不像 SOLID.KISS.DRY 原则那样,人尽皆知,但它却非常实用.利用这个原则,能够帮我们实现代码的"高内聚.松耦合".今天, ...

  6. 面向对象思想设计原则及常见设计模式

    面向对象思想设计原则 •在实际的开发中,我们要想更深入的了解面向对象思想,就必须熟悉前人总结过的面向对象的思想的设计原则 •单一职责原则 •开闭原则 •里氏替换原则 •依赖注入原则 •接口分离原则 • ...

  7. 【设计模式之美 设计原则与思想:设计原则】23 | 实战一(上):针对业务系统的开发,如何做需求分析和设计?

    对于一个工程师来说,如果要追求长远发展,你就不能一直只把自己放在执行者的角色,不能只是一个代码实现者,你还要有独立负责一个系统的能力,能端到端(end to end)开发一个完整的系统.这其中的工作就 ...

  8. IOS设计模式的六大设计原则之开放-关闭原则(OCP,Open-Close Principle)

    定义 一个软件实体(如类.模块.函数)应当对扩展开放,对修改关闭. 定义解读 在项目开发的时候,都不能指望需求是确定不变化的,大部分情况下,需求是变化的.那么如何应对需求变化的情况?这就是开放-关闭原 ...

  9. 设计模式学习总结(一)——设计原则与UML统一建模语言

    目录 一.概要 1.1.设计模式定义 1.2.设计模式分类 1.3.设计模式书籍 二.UML统一建模语言 2.1.UML分类 2.2.类图 2.2.1.关联 2.2.2.聚合/组合 2.2.3.依赖 ...

最新文章

  1. Delphi.net Chrome
  2. 1 文巾解题 191. 位1的个数
  3. 中国农民丰收节交易会新闻发布会倡导功能农业·农业大健康
  4. GMGDC专訪戴亦斌:具体解释QAMAster全面測试服务6大功能
  5. modbus tcp 入门详解
  6. C# ASP.NET B/S模式下,采用lock语法 实现多用户并发产生不重复递增单号的一种解决方法技术参考...
  7. java svn 版本号_eclipse中的Java文件自动根据svn版本号生成注释
  8. PYTHON_正则表达式
  9. Session、Dialog和Transaction的区别
  10. java 多行文本框_Swing常用组件之多行文本区JTextArea
  11. html 调用离线地图,百度地图API1.1制作的离线地图控件(html+webbroswer)
  12. 印度人在接管硅谷的时候,中国人在做什么?
  13. 自动控制原理9.1---线性系统的状态空间描述(中下)
  14. 强制清理CDN(DNS)缓存方法
  15. iphone7plus计算机,iPhone 7 Plus和iPhone 8 Plus的区别-太平洋电脑网
  16. 超全IBM MQ安装运行与代码连接测试
  17. 美丽的窗花java分形_活动设计——美丽的窗花 (设计意图及教学反思)
  18. 【Python错误】Simplify chained comparison
  19. 成就你一生的100个哲理81-90
  20. coppeliasim(vrep)设置弹簧

热门文章

  1. Tarjan算法(求强连通分量与割点)
  2. 收集国家地理杂志图片的软件
  3. python使用RCON,连接游戏服务器求生之路2、我的世界等
  4. sqlplus 违反完整约束条件 - 未找到父项关键字
  5. [20180920]航空航天与国防行业的股票排名
  6. VIPCARD微信会员开多行业多开SAAS系统
  7. 华为云服务器 windows Server 2019 数据中心版
  8. excel文件下载处理两种方法
  9. AMD Athlon64 X2 4000+ AM2 CPU 超频要点
  10. C语言编程>第二周 ⑦ 猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。