来自谈谈依赖和解耦 这篇文章的阅读笔记

何以产生的依赖?

既然要研究怎么让模块解耦,那当然要从根源来分析:依赖它到底从何而来?

接口管理

依赖其实在接口设计完成的时候就出来了,虽然这是我们自己设计的接口,但它依赖于上游按照约定来调用
而上游有调整的时候,我们是需要跟随者适配或者调整的。

依赖来自于:上游按照阅读接口调用底层,底层按照约定接口提供服务
这种情况下,依赖导致的变更是:
上游不按照底层调用了,需要底层新增方法
or 下游不按照约定api开发了新版本,并且不向下兼容,迫使上游升级 or 对底层进行模块

状态管理

由接口管理产生的依赖通常来自外部,而应用内部也会有依赖的产生,常见的包括状态管理和事件管理

状态管理:
1、生命周期:程序是否已经启动、功能是否正常运行、输入输出是否有变化等
由于程序会有状态变化,因此我们的功能实现必然依赖程序的状态,如:
只有用户登录了才能进行更多的操作、订单产生了才可以进行撤销、界面渲染完成了用户才可以点击,等等。

从代码可读性和可维护性角度来看,面向对象编程近些年来还是稍胜于函数式编程,面向对象的设计本身就是状态设计的过程,而某个对象的运行结果,也会依赖于该对象的状态。

依赖产生于:
对某个程序“按照预期运行”进行合理设计而产生的依赖。

功能管理

当我们根据功能将代码拆分成一个个模块之后,功能模块的管理也同样会产生一些依赖。

管理系统中最常见的就是面板的管理,对于每个面板来说,它应该只关心自身的状态。
产品设计会要求我们在打开某个新的面板的时候,关闭其他面板;
或是在点击面板以外的地方,关闭当前面板。
这会涉及到面板与面板以外界面的通信,一般来说我们可以使用事件的方式来管理:
每个面板在创建的时候,都需要监听外界的一个点击事件,并判断点击区域落在面板外面的时候,触发关闭。

某一天,产品提了个需求,所有的这些面板关闭的时候都要有一个动画效果,至于这个关闭动画的持续时间,要根据点击位置与面板的距离来计算。我们需要在点击事件触发的时候,把点击的位置告诉监听对象。

于是,我们全局搜了所有该类型事件的触发节点和监听节点,一一进行调整。

这是来自于对某个功能“不会发生变更”而产生的依赖。

依赖来自于约束

举个例子,前端框架中为了更清晰地管理渲染层、数据层和逻辑处理,常用的设计包括 MVC、MVVM 等。而要使这样的架构设计发挥出效果,我们需要遵守其中的使用规范,不可以在数据层里直接修改界面等。
可以看到,依赖来自于对代码的设计。

依赖如何解耦?

依赖如何划分?

无状态的函数式编程

进行状态有无的划分:纯函数、副作用函数、有状态维护的对象

写多了面向对象编程的代码,对一些状态的管理和维护感到心烦
我们需要对功能模块进行划分,划分出有状态和无状态的功能,来将状态管理放置到更小的范围,避免“牵一发而动全身”。

单向数据流管理

进行了模块内外数据的划分。

代码解耦的方式,其中也包括了使用单向数据流这种方式。

由于一个应用中,各个功能间都会有一些相互间的数据依赖,为了避免模块间的直接依赖,使用单向流的方式,可以将一些非模块内闭环的数据通过有序、单向的方式进行捆绑

模块之间的依赖解除了,调整为模块与数据流模块之间的依赖,代码的耦合程度得到缓解。

服务化

通过将功能进行业务领域的拆分,我们得到了不同领域的服务,常见的例如电商系统拆分成订单系统、购物车系统、商品系统、商家系统、支付系统等等。

而如今打得火热的“微服务”,也都是基于领域建模的一种实现方式。

在这里,我们进行了业务领域的划分。

模块化与依赖注入

相比于针对系统设计的服务化,同样有针对功能设计的模块化。

在前端领域,同样可以根据功能拆分为表单功能、列表功能、面板功能等,
通过给这些功能设置边界、封装成独立完整的模块,可以将功能与功能之间的依赖降到最低。
同样的,根据功能划分的方式,我们还可以将功能拆分成渲染层、数据层、网络层这样的模块。

而配合依赖注入的方式,
我们在使用这些功能的时候不再需要单独对这些功能的状态进行维护,同样实现了功能模块间的解耦