网站建设服务商 需要什么主机,3d家装效果图制作软件,wordpress 教育 主题,安徽省建设厅官方网站前言
《规范与重构 - 1.什么情况下要重构#xff1f;重构什么#xff1f;又该如何重构#xff1f;》讲过#xff0c;重构可以分为大规模高层重构#xff08;简称 “大型重构”#xff09;和小规模低层次重构#xff08;简称 “小型重构”#xff09;。大型重构是对系统…前言
《规范与重构 - 1.什么情况下要重构重构什么又该如何重构》讲过重构可以分为大规模高层重构简称 “大型重构”和小规模低层次重构简称 “小型重构”。大型重构是对系统、模块、代码结构、类之间关系等底层代码设计进行重构。
对于大型重构来说最有效的解决手段就是 “解耦”。解耦的目的是实现代码高内聚、松耦合。关于解耦今天准备分三个部分来给你讲解。
解耦为何如此重要如何判定代码是否需要解耦如何给代码解耦 解耦为何如此重要
软件设计与开发最重要的工作之一就是应对复杂。 人处理复杂性的能力是有限的。过于复杂的代码往往在可读性、可维护上都不友好。 那如何在控制代码的复杂性呢
手段有很多我个人认为最关键的就是解耦保证代码松耦合、高内聚。如果说重构是保证代码质量不至于腐化到无可救药的地步的有效手段那么利用解耦的方法对代码重构就是保证代码不至于复杂到无法控制的有效手段。 关于 “高内聚、松耦合”在《设计原则 - 8.迪米特法则LOD》中有介绍。 实际上 “高内聚、松耦合” 是一个比较通用的设计思想不仅可以指导细粒度的类和类之间关系的设计还能知道粗粒度的系统、架构、模块的设计。相对于编码规范它能够在更高层次上提高代码的可读性和可维护性。
不管是阅读代码还是修改代码“高内聚、松耦合” 的特性可以让我们聚焦在某一模块或类中不需要了解太多其他模块或类的代码降低了阅读和修改代码的难度。而且因为依赖关系简单耦合小修改代码不至于牵一发而动全身代码改动比较集中引入 bug 的风险也就减少了很多。同时“高内聚、松耦合” 的代码可测试性也更加好容易 mock 或者很少需要 mock 外部依赖的模块或者类。
此外代码 “高内聚、松耦合”也就意味着代码结构清晰、分层和模块化合理、依赖关系简单、模块或类之间的耦合小那代码整体的质量就不会差。即便某个类或模块设计的不怎么合理代码质量不怎么高影响的范围也是非常有限的。我们可以聚焦于某个类或模块做相应的小型重构。相对于代码结构的调整这种改动范围比较集中的小型重构的难度就容易多了。
如何判定代码是否需要解耦
怎么判断代码的耦合程度呢怎么判断代码是否符合 “高内聚、松耦合” 呢如何判断系统是否需要解耦重构呢
间接的衡量标准有很多前面我们也讲到了一些比如看修改代码会不会牵一发而动全身。此外还有一个直接的衡量办法就是把模块与模块的、类与类的依赖关系画出来根据依赖关系图的复杂性来判断是否需要解耦重构。
如果依赖关系复杂、混乱那从代码结构上来讲可读性和可维护性肯定不是太好那我们就需要考虑是否通过解耦的办法让依赖关系变得清晰、简单。当然这种判断比较主观的我们可以把它作为一种参考和梳理依赖的手段配合其他衡量标准一块来使用。
如何给代码解耦
1. 封装与抽象
封装和抽象作为两个非常通用的设计思想可以应用在很多设计场景中个比如系统、模块、lib、组件、接口、类等等。封装和抽象可以有效地隐藏实现的复杂性隔离实现的易变性给依赖模块提供稳定且易用的抽象接口。
例如Unix 的 open() 文件操作函数用起来很简单但是底层的实现是非常复杂的。我们通过将其封装成一个抽象的 open() 函数能够有效控制代码复杂性的蔓延将复杂性封装在局部代码中。此外因为 open() 函数基于抽象而非具体的实现来定义所以我们在改动 open() 的底层实现的时候并不需要修改依赖它的上层代码符合我们前面提到的 “高内聚、松耦合” 代码的评判标准。 Unix 的 open() 涉及权限控制、并发控制、物理存储等等。 2.中间层
引入中间层能简化模块和类之间的依赖关系。 上图是引入中间层前后的依赖关系图。引入数据存储中间层之前A、B、C 三个模块都要依赖内一级缓存、Redis 二级缓存、DB持久化存储。在引入数据存储中间层之后A、B、C 三个模块只需要依赖一个数据存储中间层即可。引入中间层明显地简化了依赖关系让代码结构更加清晰。 此外在进行重构的时候引入中间层可以起到过渡的作用能够让开发和重构同步进行不相互干扰。 比如某个接口设计的有问题需要先修改它的定义同时所有调用这个接口的代码都要做相应的改动。如果新开发的代码也用到这个接口那开发就跟重构冲突了。 为了让重构能小步快跑可以分四个阶段来完成接口的修改 引入一个中间层包裹老的接口提供新的接口定义。新开发的代码依赖中间层提供的新接口。将依赖老接口的代码改为调用新接口。确保所哟的代码都调用新接口之后删除老接口。 这样每个阶段的工作量都不会很大都可以在很短的时间内完成。重构跟开发冲突的概率也变小了。 3.模块化
模块化是构件复杂系统常用的手段。 不仅在软件行业在其他行业这个手段也非常有用。对于一个大型复杂系统来说没有人能掌控所有的细节。之所以我们可以搭建出如此复杂的系统并且能维护得了最主要的原因就是将系统划分成各个独立的模块让不同的人负责不同的模块这样即便在不了解全部细节的情况下管理者也能协同各个模块让这个系统有效运转。 很多大型软件比如 Windows之所以能做到几百、上千人有条不紊地协作开发也归功于模块化做的很好。不同的模块之间通过 API 来进行通信每个模块之间的耦合很小每个小的团队聚焦独立的高内聚模块来开发最终像搭积木一样将各个模块组装起来构建成一个超级复杂的系统。
聚焦于代码层面。合理的划分模块能有效地解耦代码提高代码的可读性和可维护性。我们在开发代码时一定要有模块化意识将每个模块当做一个独立的 lib 来开发只提供封装了内部实现细节的接口给其他模块使用这样可以减少不同模块之间的耦合度。
实际上从刚刚的讲解中我们可以发现模块化的思想无处不在将 SOA、微服务、lib 库、系统内模块划分甚至是类、函数的设计都体现了模块化的思想。 模块化思想更加本质的东西就是分而治之。 4.其他设计思想和原则
“高内聚、松耦合” 是一个非常重要的设计思想能够有效提高代码的可读性和可维护性缩小功能改动导致的代码范围改动。在前面章节的讲解中我们多次提到过这个涉及思想。很多设计原则都是以实现代码的 “高内聚、松耦合” 为目的。我们来一块回顾下有哪些原则。
单一职责原则
内聚性和耦合性并非独立的。高内聚会让代码更加松耦合而实现高内聚的重要指导原则就是单依职责原则。模块或者类的职责设计得单一而不是大而全那依赖它的类和它依赖的类就会比较少代码耦合也就相应的降低了。
基于接口而非实现编程
基于接口而非实现编程能通过接口这样一个中间件隔离变化和具体的实现。这样做的好处是在有依赖关系的两个模块或类之间一个模块或者类的改动不会影响到另一个模块或类。实际上这就相当于将一种强依赖关系强耦合解耦为了弱依赖关系弱耦合。
依赖注入
跟基于接口而非实现编程思想类似依赖注入也是将代码之间的强耦合变为弱耦合。尽管依赖注入无法将本应该有依赖关系的两个类解耦为没有依赖关系但可以让耦合关系没有那么紧密容易做到插拔替换。
多用组合少用继承
我们知道继承是一种强依赖关系父类与子类高度耦合且这种耦合关系非常脆弱牵一发而动全身父类的每一个改动都会影响所有的子类。相反组合关系是一种若依赖关系这种关系更加灵活所以对于继承结构比较复杂的代码利用组合来替换继承也是一种解耦的有效手段。
迪米特法则
迪米特法则讲的是不该有直接依赖关系的类不要有依赖有依赖关系的类之间尽量只依赖必要的接口。从定义上我们可以看出这条原则的目的就是为了实现代码的松耦合。至于如何应用这条原则来解耦代码你可以回头看一看《设计原则 - 8.迪米特法则LOD》。
除了上面降到的这些设计思想和原则之外还要一些设计模式也是为了解耦依赖比如观察者模式等这一部分内容后面再讲解。
回顾
1.解耦为何如此重要
过于复杂的代码往往在可读性、可维护性上都不友好。解耦保证代码松耦合、高内聚是控制代码复杂度的有效手段。代码高内聚、松耦合也意味着代码结构清晰、分层模块化合理、依赖关系简单、模块或类之间的耦合小那代码整体的质量不会差。
2.代码是否需要解耦
间接的衡量标准有很多比如看修改代码是否牵一发而动全身。直接的衡量标准是把模块与模块、类与类之间的依赖关系画出来根据依赖关系图的复杂性来判断是否要解耦重构。
3.如何给代码解耦
给代码解耦的方式有封装与抽象、中间层、模块化以及一些其他设计思想与原则比如单一职责原则、基于接口而非实现编程、依赖注入、多用组合少用继承、迪米特法则等。当然该有一些设计模式比如观察者模式。