"控制权反转",只要在软件开发行业呆着,或早或晚一定会接触到这几个字。就笔者而言,与这这个词最初的邂逅已然模糊,但对其的顿悟时至今日却依然历历在目。
1. 前言
从字面来看,“控制权反转”也就是将原本应该由A所拥有的控制权或决定权转移/移交到B或C身上。因此控制权反转也就是个职责确定的过程,即此事到底应该由谁来负责。
确定了概念之后,下一个问题马上就来了——为什么我们要思考职责划分的问题?或者说控制权反转于我们有什么好处? 其实最直接的好处在软件开发的其他原则里说得已经很清楚了,比如关注点分离,又比如单一职责。
2. 举例说明
以上的内容难免显得比较晦涩,所以接下来我将以几个实际的例子来进行说明。
2.1 Spring源码
不论是以何种编程语言为工具,当试图在该领域有所精进的时候,必然绕不开源码这一关。就笔者目前从事的Java而言,Spring的源码就是无论如何都绕不过去的了。而对于源码的学习,除了命名规范,设计模式的实际应用,底层细节的了解之外,还有一点给笔者留下深刻印象的就是这控制权的反转了。
首先需要说明的是,本文并不是源码解析,所以只会略微提及相关类,主要还是阐明思想。
了解过Spring发展历史的读者,应该都知道Spring对于ApplicationListener
加入泛型的支持是在3.x之后, 即ApplicationListener<E extends ApplicationEvent>
形式。
而在实际的时候中,Spring里事件的播发是被拆分为单独的组件, 以ApplicationEventMulticaster
接口的形式向外暴露,而我们本次的关注重点就是其抽象实现类AbstractApplicationEventMulticaster
,更准确来说是其AbstractApplicationEventMulticaster.supportsEvent()
方法——其中满足当前事件响应的合格Listener的筛选工作则是委托给了GenericApplicationListenerAdapter
(实现了SmartApplicationListener
接口)。
通过观察GenericApplicationListenerAdapter
实现的supportsEventType()
方法,我们会发现Spring并没有如我们原本所预期的那样——在AbstractApplicationEventMulticaster
中直接对各个Listener进行各种逻辑判断,进而确认其是否满足当前事件响应条件?而是在内部将判断职责全权转移给了SmartApplicationListener
实现类,然后仅仅通过回调该接口的supportsEventType()
方法来由Listener自身告诉Spring——其是否支持本次的EventType? 如此,复杂臃肿的逻辑判断代码被清晰地分割到了每个Listener中。且后续的扩展与主体逻辑无关,完全是由各个Listener自主控制。
2.2 SICP
笔者就不再重复SICP在笔者心目中的地位了,直接上例子。
在SICP视频教程的lec3a中,Harold Abelson教授向我们展示了使用程序绘制下面这幅图的方法。
image.png
对于初次接触到这个需求的读者而言,这毫无疑问会被认为是一个比较大的挑战。在他们的思路里面大概会有个核心逻辑处理中心去不断的对整幅画布进行定位,打印等操作,直到最后一块空白被占满。
教授在视频里传授或者说要传达给我们的思想当然不是这样的了,其实视频中教授想要传达给我们的其实是不要将Data和Procedure进行严格的区分,它们彼此之间的界限其实非常模糊(详细的可参见从SICP里学到的)。笔者在学习该视频时候,除了开始慢慢接受教授所要表达的意思之外,另外一大感受就是“相比较于第一时间出现在大脑里的想法,最好的方式是主控类只是负责划分出一个个的区域,而让基本元素Picture自身负责将自己绘制到指定的区域内,这样不仅主控类逻辑得到极大解放,每个基本元素也都能独立决定将自身绘制成什么样子,拥有了最大的决策权。”
2.3 IOC
只要聊到Spring,必然逃不过的一个概念就是IOC了。而巧合的是作为英文缩写的IOC,其全名翻译正是“控制权反转”,只是相较于以上两个例子里的框架将权限下放给各个使用者,IOC则是将构造对象和组织对象之间依赖关系的权限上交给了IOC容器,这也正是IOC名称的由来。
3. 结语
正如OOP从来没有将减少代码量作为自己的核心亮点,控制权反转的这一系列逻辑中,代码量并没有减少,而且很可能反而是增加的。不过它们将繁杂臃肿的逻辑判断从主控类拆分并融入到了各个子组件中,所以虽然代码量并未减少,但主控类的职责被大量减轻,主线逻辑也因此变得清爽,稳固;或者是客户端主动将部分权限移交给专门的框架来完成,在转移出去的职责被完成得更加顺畅之余,客户端也因为更加专注于自身特有需求而将其做得更好。
框架编写者初学者很容易犯的一个错就是试图帮助框架帮助使用者做尽可能多的事情,导致的一大后果就是一大堆的配置项,进而造成框架内部不断膨胀的逻辑控制语句,经过一段时间,框架编写者苦不堪言,使用者亦然。
所以,如果下次你感觉代码变得臃肿,丑陋不堪,新需求的加入变得异常困难,可以考虑下控制权反转,也许就会有出其不意,柳暗花明又一村的神奇效果。
网友评论