本文主要介绍对JavaScript系列的深入理解(22):s . o . l . I . d .五大原则的依赖反转原理详解本文讲解DIP与JavaScript,何时依赖注入等。有需要的朋友可以参考一下。
前言
本章我们要讲解的是S.O.L.I.D五原则的JavaScript语言实现的第五章,依赖倒置原则LSP(Dependency Inversion Principle)。
3358 freshbrewedcode . com/derekgerer/2012/01/22/solid-JavaScript-the-dependency-inversion-principle/
依赖倒置原则
反转原理的描述是:
A.高层模块不应该依赖低层模块。两者都应该依赖于抽象。
高层模块不应该依赖于低层模块,但两者都应该依赖于抽象。
B.抽象不应该依赖于细节。细节应该依赖于抽象。
抽象不应该依赖细节,细节应该依赖抽象。
反转原则最重要的问题是确保一个应用程序或框架的主要组件与不重要的底层组件的实现细节解耦,这将确保程序最重要的部分不会受到底层组件的更改和修改的影响。
这个原理的第一部分是关于高层模块和低层模块之间的耦合方式。在传统的拆分架构中,高层模块(封装了程序的核心业务逻辑)总是依赖于一些低层模块(一些基本点)。当应用依赖倒置的原则时,关系是颠倒的。与依赖低级模块的高级模块不同,依赖倒置使得低级模块依赖于高级模块中定义的接口。比如你想持久化一个程序的数据,传统的设计是核心模块依赖于一个持久化模块的API。按照依赖倒置原则重新配置后,核心模块需要定义持久化API接口,然后持久化实现实例需要实现核心模块定义的API接口。
这个原则的第二部分描述了抽象和细节之间的正确关系。了解C语言对理解这部分是有帮助的,因为它的适用性是显而易见的。
与一些静态类型语言不同,C不提供语言级别的概念来定义接口。类定义和类实现有什么关系?在C中,类是以头文件的形式定义的,头文件定义了源文件需要实现的类成员方法和变量。因为所有的变量和私有方法都是在头文件中定义的,所以它们可以用于抽象,与实现细节分离。通过只定义抽象方法来实现接口(C中的抽象基类),用这个概念来实现类。
DIP and JavaScript
因为JavaScript是一种动态语言,所以不需要为了解耦而进行抽象。所以抽象不应该依赖于细节。这个变化在JavaScript中并没有太大的影响,但是高层模块不应该依赖于低层模块,但是确实如此。
当在静态类型语言的上下文中讨论依赖倒置的原理时,耦合的概念包括语义和物理。也就是说,如果高级模块依赖于低级模块,那么它不仅与语义接口耦合,还与在低级模块中定义的物理接口耦合。也就是说,高层模块不仅要和第三方类库解耦,还要和原生低层模块解耦。
为了解释这一点,设想一个. NET程序可能包含一个非常有用的依赖于低级持久模块的高级模块。当作者需要给持久化API添加一个类似的接口时,无论是否使用了依赖倒置原则,都不能在其他程序中重用高层模块,直到重新实现低层模块的新接口。
在JavaScript中,依赖倒置原则的适用性只限于高级模块和低级模块之间的语义耦合。例如,DIP可以根据需要添加接口,而不是耦合底层模块定义的隐式接口。
为了理解这一点,让我们看一下下面的例子:
复制代码如下:
. fn . track map=function(options){
var默认值={
/*默认值*/
};
选项=$。扩展({},默认值,选项);
var mapOptions={
中心:新的谷歌。地图。锁定(选项。纬度,选项。经度),
变焦:12,
地图类型id:Google。地图。映射类型id。路标
},
map=new google.maps.Map(this[0],mapOptions),
pos=新谷歌。地图。锁定(选项。纬度,选项。经度);
var marker=新谷歌。地图。标记({
位置:位置,
标题:options.title,
图标:选项。图标
});
marker.setMap(地图);
options.feed.update(函数(纬度,经度){
马克笔。设置映射(空);
var new lat LNG=新谷歌。地图。lat液化天然气(纬度,经度);
马克笔。位置=最新液化天然气;
marker.setMap(地图);
地图。设置中心(新定位);
});
还这个;
};
var updater=(function() {
//私有属性
返回{
更新:函数(回调){
updateMap=回调
}
};
})();
$('#map_canvas ').轨迹图({
纬度:35.04640193770725,
经度:-89.9193264007568,
偶像:' http://bit.ly/zjnGDe ',
标题:"跟踪编号:12345",
提要:更新程序
});
在上述代码里,有个小型的射流研究…类库将一个差异转化成地图以便显示当前跟踪的位置信息轨迹图。函数有2个依赖:第三方的谷歌地图应用程序接口和位置馈送。该饲料对象的职责是当图标位置更新的时候调用一个回收回调(在初始化的时候提供的)并且传入纬度纬度和精度经度。谷歌地图应用程序接口是用来渲染界面的。
饲料对象的接口可能按照装,也可能没有照装轨迹图函数的要求去设计,事实上,他的角色很简单,着重在简单的不同实现,不需要和谷歌地图这么依赖。介于轨迹图语义上耦合了谷歌地图API,如果需要切换不同的地图提供商的话那就不得不对轨迹图函数进行重写以便可以适配不同的提供商。
为了将于谷歌地图类库的语义耦合翻转过来,我们需要重写设计轨迹图函数,以便对一个隐式接口(抽象出地图提供商供应者的接口)进行语义耦合,我们还需要一个适配谷歌地图应用程序接口的一个实现对象,如下是重构后的轨迹图函数:
复制代码代码如下:
$.fn。轨迹图=功能(选项){
定义变量默认值={
/*默认值*/
};
选项=$。扩展({},默认值,选项);
options.provider.showMap(
这个[0],
选项。纬度,
选项。经度,
选项。图标,
选项。标题);
options.feed.update(函数(纬度,经度){
options.provider.updateMap(纬度,经度);
});
还这个;
};
$('#map_canvas ').轨迹图({
纬度:35.04640193770725,
经度:-89.9193264007568,
偶像:' http://bit.ly/zjnGDe ',
标题:"跟踪编号:12345",
提要:更新程序,
提供者:trackMap.googleMapsProvider
});
在该版本里,我们重新设计了轨迹图函数以及需要的一个地图提供商接口,然后将实现的细节挪到了一个单独的谷歌地图提供者组件,该组件可能独立封装成一个单独的Java脚本语言模块。如下是我的谷歌地图提供者实现:
复制代码代码如下:
轨迹图。谷歌地图提供商=(function(){
定义变量标记,地图;
返回{
显示地图:函数(元素,纬度,经度,图标,标题){
var mapOptions={
中心:新的google.maps.LatLng(纬度,经度),
变焦:12,
地图类型id:Google。地图。映射类型id。路标
},
pos=new google.maps.LatLng(纬度,经度);
map=new google.maps.Map(元素,地图选项);
marker=new google.maps.Marker({
位置:位置,
标题:标题,
图标:图标
});
marker.setMap(地图);
},
更新地图:函数(纬度,经度){
马克笔。设置映射(空);
var new lat LNG=新谷歌。地图。lat液化天然气(纬度,经度);
马克笔。位置=最新液化天然气;
marker.setMap(地图);
地图。设置中心(新定位);
}
};
})();
做了上述这些改变以后,轨迹图函数将变得非常有弹性了,不必依赖于谷歌地图API,相反可以任意替换其它的地图提供商,那就是说可以按照程序的需求去适配任何地图提供商。
何时依赖注入?
不太相关。事实上,依赖注入的概念经常与依赖倒置的原理相混淆。为了澄清这种区别,我们有必要解释一下:
依赖注入是控制反转的一种特殊形式。反转意味着一个组件如何获得它的依赖。依赖注入意味着依赖被提供给组件,而不是被组件获取。意思是创建依赖的实例,通过工厂请求依赖,通过服务定位器或者组件本身的初始化请求依赖。依赖反转原理和依赖注入都是关于依赖的,都是用于反转的。但是,依赖倒置的原理并不关注组件如何获取依赖,而只关注高层模块如何与低层模块解耦。从某种意义上说,依赖反转的原理是控制反转的另一种形式。这里颠倒的是哪个模块定义了接口(从低层到高层)。
总结
这是五项原则中的最后一项。在这五个字里,我们看到了SOLID是如何在JavaScript中实现的,在JavaScript中从不同的角度阐述了不同的原理。(大叔注:其实大叔觉得虽然不伦不类,但是在另一个层面上,总的原则其实在所有语言中都是一样的。)
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。