“依赖方向应指向稳定方”
有的开发者写代码前会画图,有的开发者则不会。
但不管画不画图,其实多数开发者并不知道画图能为开发提供什么帮助。
今天这篇文章,会聊一聊画图对开发的一些支持作用。
本篇文章适合以下小伙伴们观看:
- 看不懂UML类图
- 不知道画图对写代码有什么好处
好了,前言到此为止,正文开始!
依赖与稳定依赖
其实提到依赖,最常见的是UML图中的类图。
类图中充满了”依赖“这个词。
关于“依赖”的类图大概是这样:
简单解释一下图中的符号含义:
- 加号/减号:代表访问控制标识符。简单点说:加号相当于public,减号相当于private。
- 虚线和箭头:代表依赖和依赖方向。图中意为A依赖B。依赖原因是A中的method2使用了B作为返回值,所以依赖了B。
这是在UML类图中的“依赖”。
我在这里聊的“依赖”范围更广一些,最起码类图中的各种关系线条都称作依赖。
比如:
但其实它们对分析的作用都一样,都是只关注依赖方和被依赖方。
这里都做一个简化,画成这样:
上图中意为A依赖B。用来概括上面那些各种类型的依赖。
现在,就要说它的用处了!注意看哦
现在有这么3个模块:A、B、C。
其中,A与C依赖B。
在开发中还是挺常见的吧?
emm,那现在思考一下,如果A、B、C分别发生了变化时,它们的影响范围分别是多少?
这里以影响的模块数量作为单位吧。比如,两个模块发生变化了,就标记为2。
先给个小提示:被依赖的模块变化时,依赖方的代码要不要调整呢?
写下来,或者画画图都可以(要不然反复思考和回忆还真挺累的)。
算好了吗?
我这里也画一下:
影响范围分别是:
- A:1个模块(A本身)
- B:3个模块(ABC)
- C:1个模块(C本身)
发现了点东西吧?
B模块的变化影响了3个模块!这相当于几倍的A与C的影响范围了!
而且,图中举例的依赖方很少,如果依赖者更多,则带来的影响范围更可怕!
所以怎么样能降低影响呢?
有时候这个B还必须改(写完发现有谐音 哈哈哈)
那咱们就降低B模块的修改频度,必须改可以,但要少改,就少影响一点。
然后同时,把频繁变化的东西从B模块里面拿走(这个操作也称作分离变与不变)。
双管齐下,它就变成了低频度变化的模块。
然后,除了这个方法,剩下的就是文章标题:稳定依赖
就是说,依赖方向应指向稳定方。
它稳定了,就不容易变化。所以依赖它的那些模块也就不会频繁受到它的影响,也就稳定多了。
这也是为什么要“稳定依赖”的原因。
小例子:接口的依赖方向为什么这样画?
接口和实现类的类图大概是这个样子的。
大家可能看过也可能没看过。
不过都没关系,我解释一下它的含义。
它表示:类AImpl 实现 接口A
大家可能也没有思考过为什么它长成这样(而不是反过来)。
也可能因为规定就这么画了:实现类指向接口,线型虚线+三角箭头。
但上面的文章中其实提到了这么做的关键。
这里停一下,想一想哈。
……
揭晓答案了:因为接口是被依赖方。一旦接口变化,实现类会受影响。
这也是有经验的开发者会经常说:接口不要随意变化。或者他们不说话,但做接口设计时,尽可能让接口稳定。
因为接口是被依赖方。而且同时被外部和内部依赖。
这里以web中的接口为例,会不会更好理解?
一个web接口的被依赖情况:
图中接口被多方依赖,所以接口一旦变化,影响范围就会很大。
影响传递
上面的图文看完了,应该对依赖和依赖方向比较熟悉了。
那现在聊聊影响传递。
现在有一个软件系统,它模块间的依赖方向是这样的:
图中,被点包起来的模块将会发生变化,让我们来看看影响情况
一点一点看哈,先看图例。白色方块代表未被影响的模块,黑色方块代表被影响的模块。
读图方向为从左到右。
从图中可以看到,影响是会传递的。最终甚至会传递到最上层(最远端)。
而且,一步一步的顺序图中可以看出:影响是沿着依赖箭头的反方向传递的。
那现在,你可能会有疑问了,影响传递威力这么大,那岂不是被依赖的底层永远都不能改了?
那当然也不是,下面让我们看看解决办法。
解决影响传递的方法:版本隔离
其实大家应该在开发中使用过这种解决方案。
比如,无论是node、maven,都会依赖指定版本号的模块(而不是任意版本)。
而且,如果是在多人协作的项目中,版本号一般是不允许随意修改的。
这其实是一种解决影响传递的方案。毕竟不变就不会被影响了嘛(笑)。
包括有些重要的接口,会有版本标记(比如v1、v2这种),其实道理也是一样的。
画图举例:
一样,先看图例:
- 空心A:代表模块A的旧版本(V1)
- 有颜色的A:代表模块A的新版本(V2)
- 被圈上的空心A:代表模块A将会发生变化
- 涂色的菱形:代表依赖A的新特性的模块
图中,新增的菱形模块依赖A的新特性。此时按理修改底层模块A一定会影响依赖它的很多模块。
为了消除影响传递带来的风险,这里使用版本隔离的方法解决问题。
新增了一个V2版本的模块,为菱形模块进行支持,新特性放在这里。
仍保留旧模块A的代码与功能。之前大块的功能都依赖旧A模块,这样旧A模块不变,就不会产生影响传递的效果。
所以就如图中一样:
依赖A模块的其他模块没被影响,而菱形模块也用到了A模块的新特性。
这样,既缩小了影响范围(V1),又用新特性支持了新模块(V2)。
那,此时完整的A模块在图中应该表述为:
即:它们两块(A模块的V1和V2版本)合起来,才是现在A模块的全部。
总结
稳定依赖原则:依赖方向应该由不稳定指向稳定方(这样变更的影响范围更小)。
影响传递方向:变更的影响随着依赖方向反向传递。
被依赖方可以使用版本隔离的方式来减小变更带来的影响范围。
内容还感兴趣吗?公众号中会有更多相关内容持续更新哦