深入理解JavaScript系列(21):S.O.L.I.D五大原则之接口隔离原则ISP详解


Posted in Javascript onMarch 05, 2015

前言

本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第4篇,接口隔离原则ISP(The Interface Segregation Principle)。

英文原文:http://freshbrewedcode.com/derekgreer/2012/01/08/solid-javascript-the-interface-segregation-principle/
注:这篇文章作者写得比较绕口,所以大叔理解得也比较郁闷,凑合着看吧,别深陷进去了
接口隔离原则的描述是:

Clients should not be forced to depend on methods they do not use.

不应该强迫客户依赖于它们不用的方法。

当用户依赖的接口方法即便只被别的用户使用而自己不用,那它也得实现这些接口,换而言之,一个用户依赖了未使用但被其他用户使用的接口,当其他用户修改该接口时,依赖该接口的所有用户都将受到影响。这显然违反了开闭原则,也不是我们所期望的。

接口隔离原则ISP和单一职责有点类似,都是用于聚集功能职责的,实际上ISP可以被理解才具有单一职责的程序转化到一个具有公共接口的对象。

JavaScript接口

JavaScript下我们改如何遵守这个原则呢?毕竟JavaScript没有接口的特性,如果接口就是我们所想的通过某种语言提供的抽象类型来建立contract和解耦的话,那可以说还行,不过JavaScript有另外一种形式的接口。在Design Patterns ? Elements of Reusable Object-Oriented Software一书中我们找到了接口的定义:
http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612

一个对象声明的任意一个操作都包含一个操作名称,参数对象和操作的返回值。我们称之为操作符的签名(signature)。
一个对象里声明的所有的操作被称为这个对象的接口(interface)。一个对象的接口描绘了所有发生在这个对象上的请求信息。
不管一种语言是否提供一个单独的构造来表示接口,所有的对象都有一个由该对象所有属性和方法组成的隐式接口。参考如下代码:

var exampleBinder = {};

exampleBinder.modelObserver = (function() {

    /* 私有变量 */

    return {

        observe: function(model) {

            /* 代码 */

            return newModel;

        },

        onChange: function(callback) {

            /* 代码 */

        }

    }

})();
exampleBinder.viewAdaptor = (function() {

    /* 私有变量 */

    return {

        bind: function(model) {

            /* 代码 */

        }

    }

})();
exampleBinder.bind = function(model) {

    /* 私有变量 */

    exampleBinder.modelObserver.onChange(/* 回调callback */);

    var om = exampleBinder.modelObserver.observe(model);

    exampleBinder.viewAdaptor.bind(om);

    return om;

};

上面的exampleBinder类库实现的功能是双向绑定。该类库暴露的公共接口是bind方法,其中bind里用到的关于change通知和view交互的功能分别是由单独的对象modelObserver和viewAdaptor来实现的,这些对象从某种意义上来说就是公共接口bind方法的具体实现。

尽管JavaScript没有提供接口类型来支持对象的contract,但该对象的隐式接口依然能当做一个contract提供给程序用户。

ISP与JavaScript

我们下面讨论的一些小节是JavaScript里关于违反接口隔离原则的影响。正如上面看到的,JavaScript程序里实现接口隔离原则虽然可惜,但是不像静态类型语言那样强大,JavaScript的语言特性有时候会使得所谓的接口搞得有点不粘性。

堕落的实现

在静态类型语言语言里,导致违反ISP原则的一个原因是堕落的实现。在Java和C#里所有的接口里定义的方法都必须实现,如果你只需要其中几个方法,那其他的方法也必须实现(可以通过空实现或者抛异常的方式)。在JavaScript里,如果只需要一个对象里的某一些接口的话,他也解决不了堕落实现这个问题,虽然不用强制实现上面的接口。但是这种实现依然违反了里氏替换原则。

var rectangle = {

    area: function() {

        /* 代码 */

    },

    draw: function() {

        /* 代码 */

    }

};
var geometryApplication = {

    getLargestRectangle: function(rectangles) {

        /* 代码 */

    }

};
var drawingApplication = {

    drawRectangles: function(rectangles) {

       /* 代码 */

    }

};

当一个rectangle替代品为了满足新对象geometryApplication的getLargestRectangle 的时候,它仅仅需要rectangle的area()方法,但它却违反了LSP(因为他根本用不到其中drawRectangles方法才能用到的draw方法)。

静态耦合

静态类型语言里的另外一个导致违反ISP的原因是静态耦合,在静态类型语言里,接口在一个松耦合设计程序里扮演了重大角色。不管是在动态语言还是在静态语言,有时候一个对象都可能需要在多个客户端用户进行通信(比如共享状态),对静态类型语言,最好的解决方案是使用Role Interfaces,它允许用户和该对象进行交互(而该对象可能需要在多个角色)作为它的实现来对用户和无关的行为进行解耦。在JavaScript里就没有这种问题了,因为对象都被动态语言所特有的优点进行解耦了。

语义耦合

导致违反ISP的一个通用原因,动态语言和静态类型语言都有,那就是语义耦合,所谓语义耦合就是互相依赖,也就是一个对象的行为依赖于另外一个对象,那就意味着,如果一个用户改变了其中一个行为,很有可能会影响另外一个使用用户。这也违反单一职责原则了。可以通过继承和对象替代来解决这个问题。

可扩展性

另外一个导致问题的原因是关于可扩展性,很多人在举例的时候都会举关于callback的例子用来展示可扩展性(比如ajax里成功以后的回调设置)。如果想这样的接口需要一个实现并且这个实现的对象里有很多熟悉或方法的话,ISP就会变得很重要了,也就是说当一个接口interface变成了一个需求实现很多方法的时候,他的实现将会变得异常复杂,而且有可能导致这些接口承担一个没有粘性的职责,这就是我们经常提到的胖接口。

总结

JavaScript里的动态语言特性,使得我们实现非粘性接口的影响力比静态类型语言小,但接口隔离原则在JavaScript程序设计模式里依然有它发挥作用的地方。

Javascript 相关文章推荐
javascript中不等于的代码是什么怎么写
Dec 29 Javascript
使用GruntJS构建Web程序之安装篇
Jun 04 Javascript
js设置控件的隐藏与显示的两种方法
Aug 21 Javascript
jquery实现页面关键词高亮显示的方法
Mar 12 Javascript
Webwork 实现文件上传下载代码详解
Feb 02 Javascript
Node.js配合node-http-proxy解决本地开发ajax跨域问题
Aug 31 Javascript
vue.js学习笔记:如何加载本地json文件
Jan 17 Javascript
微信小程序 开发经验整理
Feb 15 Javascript
vuex与组件联合使用的方法
May 10 Javascript
给localStorage设置一个过期时间的方法分享
Nov 06 Javascript
layui table 表格模板按钮的实例代码
Sep 21 Javascript
Javascript操作select控件代码实例
Feb 14 Javascript
深入理解JavaScript系列(19):求值策略(Evaluation strategy)详解
Mar 05 #Javascript
如何实现chrome浏览器关闭页面时弹出“确定要离开此面吗?”
Mar 05 #Javascript
深入理解JavaScript系列(18):面向对象编程之ECMAScript实现
Mar 05 #Javascript
基于zepto.js实现仿手机QQ空间的大图查看组件ImageView.js详解
Mar 05 #Javascript
基于jQuery实现网页进度显示插件
Mar 04 #Javascript
基于jQuery实现仿淘宝套餐选择插件
Mar 04 #Javascript
js实现类似于add(1)(2)(3)调用方式的方法
Mar 04 #Javascript
You might like
php+mysql+jquery实现简易的检索自动补全提示功能
2017/04/15 PHP
php处理多图上传压缩代码功能
2018/06/13 PHP
使用git迁移Laravel项目至新开发环境的步骤详解
2020/04/06 PHP
文本框回车提交与禁止提交示例
2013/09/27 Javascript
100个不能错过的实用JS自定义函数
2014/03/05 Javascript
介绍JavaScript的一个微型模版
2015/06/24 Javascript
webix+springmvc session超时跳转登录页面
2016/10/30 Javascript
Vue2.0用户权限控制解决方案的示例
2018/02/10 Javascript
angular2路由之routerLinkActive指令【推荐】
2018/05/30 Javascript
vue slots 组件的组合/分发实例
2018/09/06 Javascript
vue使用i18n实现国际化的方法详解
2019/09/05 Javascript
JS实现吸顶特效
2020/01/08 Javascript
[12:36]《DOTA2》国服注册与激活指南全攻略
2013/04/28 DOTA
[01:15]PWL S2开团时刻第二期——他们杀 我就白给
2020/11/25 DOTA
Python设计模式之观察者模式简单示例
2018/01/10 Python
Python 查看文件的读写权限方法
2018/01/23 Python
十分钟利用Python制作属于你自己的个性logo
2018/05/07 Python
基于python批量处理dat文件及科学计算方法详解
2018/05/08 Python
Python实现绘制双柱状图并显示数值功能示例
2018/06/23 Python
python-opencv 将连续图片写成视频格式的方法
2019/01/08 Python
Pandas之排序函数sort_values()的实现
2019/07/09 Python
基于python调用psutil模块过程解析
2019/12/20 Python
python Tensor和Array对比分析
2020/01/08 Python
使用pygame实现垃圾分类小游戏功能(已获校级二等奖)
2020/07/23 Python
Python批量获取并保存手机号归属地和运营商的示例
2020/10/09 Python
html5用video标签流式加载的实现
2020/05/20 HTML / CSS
美国在线工具商店:Acme Tools
2018/06/26 全球购物
在weblogic中发布ejb需涉及到哪些配置文件
2012/01/17 面试题
中药专业毕业自荐书范文
2014/02/08 职场文书
原材料检验岗位职责
2014/03/15 职场文书
国际商务专业求职信
2014/07/15 职场文书
新疆民族团结演讲稿
2014/08/27 职场文书
升学宴答谢词
2015/01/05 职场文书
贷款工作证明模板
2015/06/12 职场文书
安全教育主题班会教案
2015/08/12 职场文书
高中历史教学反思
2016/02/19 职场文书