很难得写点偏技术层面的东西。
在传统软件工程,特别是面向对象软件工程实践中,经常会提到“控制反转(IoC)”和“依赖注入(DI)”这样的名词。啥意思呢?解释起来其实不太容易。
我举一个不太恰当,但稍微能描述一下的类比,比如一台笔记本电脑,它需要什么功能?制造的时候并不清楚用户要怎么用,如果内置了所有的功能,那么这个电脑会无比复杂,且不实用,绝大部分人需要的只是很少的一部分功能。你看,有的人需要大容量的存储,有的人需要机械键盘,有的人需要读写光盘,有的人需要高品质摄像头和麦克风等等。有的功能即使在笔记本电脑里已经支持了,但如果需要升级和调整会非常麻烦。可是大部分时候,我们并不会因此有太大的困扰,因为 usb 接口解决了很多问题,很多功能没必要一开始就内置在电脑中,或者只需要内置普通的设备,如果用户有需要,可以自行购置外设通过 usb 接口连接在电脑上,也一样能够使用。
在这个示例里,对于电脑来说,各种硬件提供的功能就是“依赖”,它做一些特定的事情的时候,需要依赖这些硬件设备才能完成。如果所有的“依赖”都包含在“电脑”这个范畴里,那么这么一个体系会非常臃肿,如果想更换一个硬件,或增加一个硬件,那可能是牵一发动全身的。而如果很多“依赖”是通过 usb 接口提供的,对于电脑而言,它的“依赖”其实变成了 usb 接口,而每当一个设备插入 usb 接口时,相当于向电脑“注入”了一个“依赖”,依赖的变更、增加不会再增加电脑的复杂度了,灵活性极大地提升了。
最近我们的项目中遇到一个困境,让我感觉不舒服的有两点:
- 一些业务方提过来的需求非常具有定制性,可能很多其他使用者会对此无感,甚至影响使用;
- 很多需求并非核心功能,但也特别复杂,需要动到系统中的很多模块,在多人协作和多个需求并行的时候很容易产生代码冲突,不好维护。
我现在考虑的解法是设计一套灵活性高的插件系统,把这类业务定制性强、非核心的功能,甚至一些核心的功能都抽象成插件。在软件运行的各个阶段,定义一系列的时间节点,当运行到某个时间节点时,软件问问插件系统,这个时间点有哪些插件需要做点额外的事。对,每个插件做的事也很简单,就是在插件系统里注册自己,告诉插件系统,自己需要在哪些时间点做什么事就可以了。这样整个系统的就不再对那些不通用、复杂的功能有依赖关系了,转而都去依赖插件系统,而插件系统其实是稳定的,不会经常变动的,于是所有的功能就可以完成解耦。
当然想法也许是好的,但插件系统的设计和完善也是一个过程,希望这个时间成本的花费是值得的。