【Java设计模式 规范与重构】 三 大型重构的手段:高内聚,低耦合

简介: 【Java设计模式 规范与重构】 三 大型重构的手段:高内聚,低耦合

之前通过两篇Blog了解了重构的目的、内容、时机、方法、保障,了解了持续重构的重要性和好处,知道了单元测试对于重构代码的保障作用,同时明确小范围重构依照规范随时进行,而大型重构需要分层、模块化、解耦、抽象可复用组件等手段,有计划的进行,本篇Blog继续学习如何应对大型重构,核心方式就是践行高内聚,低耦合,“高内聚、松耦合”是一个比较通用的设计思想,不仅可以指导细粒度的类和类之间关系的设计,还能指导粗粒度的系统、架构、模块的设计。相对于编码规范,它能够在更高层次上提高代码的可读性和可维护性

代码解耦的好处

高内聚、松耦合在很多场景下都有很好的效果:

  • 阅读代码时:高内聚、松耦合的特性可以让我们聚焦在某一模块或类中,不需要了解太多其他模块或类的代码,焦点不至于过于发散,降低了阅读难度
  • 修改代码时:因为依赖关系简单,耦合小,改动不至于牵一发而动全身,代码改动比较集中,引入 bug 的风险也就减少了很多
  • 测试代码时:代码可测试性也更加好,容易 mock 或者很少需要 mock 外部依赖的模块或者类
  • 重构代码时:代码“高内聚、松耦合”,也就意味着,代码结构清晰、分层和模块化合理、依赖关系简单、模块或类之间的耦合小,那代码整体的质量就不会差。即便某个具体的类或者模块设计得不怎么合理,代码质量不怎么高,影响的范围是非常有限的。我们可以聚焦于这个模块或者类,做相应的小型重构。而相对于代码结构的调整,这种改动范围比较集中的小型重构就容易多了

总而言之高内聚低耦合的代码阅读和修改更方便,测试也更容易,重构也能更聚焦。过于复杂的代码往往在可读性、可维护性上都不友好。解耦保证代码松耦合、高内聚,是控制代码复杂度的有效手段。代码高内聚、松耦合,也就是意味着,代码结构清晰、分层模块化合理、依赖关系简单、模块或类之间的耦合小,那代码整体的质量就不会差。

代码是否需要解耦

代码是否需要解耦,间接的衡量标准有很多,比如,看修改代码是否牵一发而动全身、看代码是不是杂乱无章。直接的衡量标准是把模块与模块、类与类之间的依赖关系画出来,根据依赖关系图的复杂性来判断是否需要解耦重构。

如何给代码解耦

通过如下手段给代码解耦

1 封装与抽象

封装抽象作为两个非常通用的设计思想,可以应用在很多设计场景中,比如系统、模块、lib、组件、接口、类等等的设计。封装和抽象可以有效地隐藏实现的复杂性,隔离实现的易变性,给依赖的模块提供稳定且易用的抽象接口

2 引入中间层

引入中间层有点像二次封装,也可以理解为版本兼容。重构的时候引入中间层可以起到过渡的作用,能够让开发和重构同步进行,不互相干扰。比如,某个接口设计得有问题,我们需要修改它的定义,同时,所有调用这个接口的代码都要做相应的改动。如果新开发的代码也用到这个接口,那开发就跟重构冲突了。为了让重构能小步快跑,可以分下面四个阶段来完成接口的修改。

  • 第一阶段:引入一个中间层,包裹老的接口,提供新的接口定义
  • 第二阶段:新开发的代码依赖中间层提供的新接口
  • 第三阶段:将依赖老接口的代码改为调用新接口
  • 第四阶段:确保所有的代码都调用新接口之后,删除掉老的接口

这样,每个阶段的开发工作量都不会很大,都可以在很短的时间内完成。重构跟开发冲突的概率也变小了。这里可以看做适配器模式来理解,新的接口定义就是目标接口,适配者类就是之前的实现接口,中间层就是适配器类,中间层提供新的的接口实现,逐步复用旧的功能实现并重构代码,这样新旧接口并存,开发时依赖旧接口,重构时逐步替换为新接口,两不耽误。

3 模块化项目

模块化是构建复杂系统常用的手段

  • 聚焦到软件开发上,很多大型软件(比如 Windows)之所以能做到几百、上千人有条不紊地协作开发,也归功于模块化做得好。不同的模块之间通过 API 来进行通信,每个模块之间耦合很小,每个小的团队聚焦于一个独立的高内聚模块来开发,最终像搭积木一样将各个模块组装起来,构建成一个超级复杂的系统
  • 聚焦到代码层面上。合理地划分模块能有效地解耦代码,提高代码的可读性和可维护性。所以,开发代码的时一定要有模块化意识,将每个模块都当作一个独立的 lib 一样来开发,只提供封装了内部实现细节的接口给其他模块使用,这样可以减少不同模块之间的耦合度
    模块化的思想无处不在,像 SOA、微服务、lib 库、系统内模块划分,甚至是类、函数的设计,都体现了模块化思想。追本溯源,模块化思想更加本质的东西就是分而治之

4 设计思想、原则、模式

高内聚、松耦合是一个非常重要的设计思想,能够有效提高代码的可读性和可维护性,缩小功能改动导致的代码改动范围。很多设计原则都以实现代码的“高内聚、松耦合”为目的

  • 单一职责原则SRP,内聚性和耦合性并非独立的。高内聚会让代码更加松耦合,而实现高内聚的重要指导原则就是单一职责原则。模块或者类的职责设计得单一,而不是大而全,那依赖它的类和它依赖的类就会比较少,代码耦合也就相应的降低了
  • 基于接口而非实现编程,基于接口而非实现编程能通过接口这样一个中间层,隔离变化和具体的实现。这样做的好处是,在有依赖关系的两个模块或类之间,一个模块或者类的改动,不会影响到另一个模块或类。实际上,这就相当于将一种强依赖关系(强耦合)解耦为了弱依赖关系(弱耦合)
  • 接口隔离原则ISP,ISP原则是从调用者的角度出发确保接口的设计具备对调用者有良好的隔离性,使调用者只依赖自己需要的接口
  • 迪米特法则LOD:迪米特法则讲的是,不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口。从定义上明显可以看出,这条原则的目的就是为了实现代码的松耦合。
  • 依赖注入,跟基于接口而非实现编程思想类似,依赖注入也是将代码之间的强耦合变为弱耦合。尽管依赖注入无法将本应该有依赖关系的两个类,解耦为没有依赖关系,但可以让耦合关系没那么紧密,容易做到插拔替换。
  • 多用组合少用继承,继承是一种强依赖关系,父类与子类高度耦合,且这种耦合关系非常脆弱,牵一发而动全身,父类的每一次改动都会影响所有的子类。相反,组合关系是一种弱依赖关系,这种关系更加灵活,所以,对于继承结构比较复杂的代码,利用组合来替换继承,也是一种解耦的有效手段。

除了这些设计思想、原则,很多设计模式也是高内聚、松耦合的具体体现。

总结一下

讨论解耦的背后其实主要讨论高内聚、松耦合这个比较通用的设计思想,它不仅可以指导细粒度的类和类之间关系的设计,还能指导粗粒度的系统、架构、模块的设计。相对于编码规范,它能够在更高层次上提高代码的可读性和可维护性,是大型重构的重要手段,从面向对象设计思想层面角度就是:封装、抽象,从思想的最佳实践角度就是基于接口而非实现编程、多用组合少用继承,从设计原则层面角度就是:SRP、ISP、LOD,从设计模式角度就是:观察者模式、适配器模式等。从系统设计角度就是:模块化,从编程技巧角度就是:DI依赖注入,从学习设计模式迄今为止了解了封装、继承、多态、抽象、IOC依赖反转、高内聚-低耦合这几个设计思想。顺便区分下:设计思想、设计原则、设计模式、编码技巧、编码规范、系统设计这几种概念,世界线收束了!

相关文章
|
16小时前
|
Java
Java 编程风格与规范:提升代码质量与可维护性
【4月更文挑战第27天】在软件开发过程中,良好的代码规范和一致的编程风格对于团队协作、代码可读性和可维护性至关重要。Java 作为一种流行的编程语言,拥有丰富的编码规范和最佳实践。
7 2
|
3天前
|
设计模式 算法 Java
[设计模式Java实现附plantuml源码~行为型]定义算法的框架——模板方法模式
[设计模式Java实现附plantuml源码~行为型]定义算法的框架——模板方法模式
|
3天前
|
设计模式 JavaScript Java
[设计模式Java实现附plantuml源码~行为型] 对象状态及其转换——状态模式
[设计模式Java实现附plantuml源码~行为型] 对象状态及其转换——状态模式
|
3天前
|
设计模式 存储 JavaScript
[设计模式Java实现附plantuml源码~创建型] 多态工厂的实现——工厂方法模式
[设计模式Java实现附plantuml源码~创建型] 多态工厂的实现——工厂方法模式
|
3天前
|
设计模式 Java Go
[设计模式Java实现附plantuml源码~创建型] 集中式工厂的实现~简单工厂模式
[设计模式Java实现附plantuml源码~创建型] 集中式工厂的实现~简单工厂模式
|
4天前
|
设计模式 存储 前端开发
Java从入门到精通:2.2.1学习Java Web开发,了解Servlet和JSP技术,掌握MVC设计模式
Java从入门到精通:2.2.1学习Java Web开发,了解Servlet和JSP技术,掌握MVC设计模式
|
10天前
|
设计模式 算法 Java
Java中的设计模式及其应用
【4月更文挑战第18天】本文介绍了Java设计模式的重要性及分类,包括创建型、结构型和行为型模式。创建型模式如单例、工厂方法用于对象创建;结构型模式如适配器、组合关注对象组合;行为型模式如策略、观察者关注对象交互。文中还举例说明了单例模式在配置管理器中的应用,工厂方法在图形编辑器中的使用,以及策略模式在电商折扣计算中的实践。设计模式能提升代码可读性、可维护性和可扩展性,是Java开发者的必备知识。
|
13天前
|
设计模式 算法 Java
小谈设计模式(30)—Java设计模式总结
小谈设计模式(30)—Java设计模式总结
|
19天前
|
设计模式 SQL 算法
设计模式了解哪些,模版模式
设计模式了解哪些,模版模式
19 0
|
1月前
|
设计模式 Java uml
C++设计模式之 依赖注入模式探索
C++设计模式之 依赖注入模式探索
37 0