深入理解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 相关文章推荐
jquery 插件学习(五)
Aug 06 Javascript
CSS(js)限制页面显示的文本字符长度
Dec 27 Javascript
密码强度检测效果实现原理与代码
Jan 04 Javascript
jquery获取html元素的绝对位置和相对位置的方法
Jun 20 Javascript
JS建造者模式基本用法实例分析
Jun 30 Javascript
Angularjs 实现一个幻灯片示例代码
Sep 08 Javascript
Javascript中数组去重与拍平的方法示例
Feb 03 Javascript
详解在Vue中有条件地使用CSS类
Sep 30 Javascript
vue的toast弹窗组件实例详解
May 14 Javascript
使用layui+ajax实现简单的菜单权限管理及排序的方法
Sep 10 Javascript
Node.js API详解之 timer模块用法实例分析
May 07 Javascript
如何在vue中使用HTML 5 拖放API
Jan 14 Vue.js
深入理解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 session_start()关于Cannot send session cache limiter - headers already sent错误解决方法
2009/11/27 PHP
CURL的学习和应用(附多线程实现)
2013/06/03 PHP
thinkPHP自定义类实现方法详解
2016/11/30 PHP
User Scripts: Video Download by User Scripts
2007/05/14 Javascript
JavaScript ECMA-262-3 深入解析.第三章.this
2011/09/28 Javascript
将list转换为json失败的原因
2013/12/17 Javascript
javascript页面加载完执行事件代码
2014/02/11 Javascript
javascript字符串与数组转换汇总
2015/05/26 Javascript
kindeditor编辑器点中图片滚动条往上顶的bug
2015/07/05 Javascript
jquery输入数字随机抽奖特效的简单实现代码
2016/06/10 Javascript
解决wx.onMenuShareTimeline出现的问题
2016/08/16 Javascript
javascript 分号总结及详细介绍
2016/09/24 Javascript
ECMAScript6--解构
2017/03/30 Javascript
详解JavaScript调用栈、尾递归和手动优化
2017/06/03 Javascript
JavaScript中立即执行函数实例详解
2017/11/04 Javascript
jQuery除指定区域外点击任何地方隐藏DIV功能
2017/11/13 jQuery
vue 将页面公用的头部组件化的方法
2017/12/18 Javascript
Vue Socket.io源码解读
2018/02/07 Javascript
使用JS实现导航切换时高亮显示的示例讲解
2018/08/22 Javascript
解决vant title-active-color与title-inactive-color不生效问题
2020/11/03 Javascript
python递归查询菜单并转换成json实例
2017/03/27 Python
PyQt5实现五子棋游戏(人机对弈)
2020/03/24 Python
PyQt5组件读取参数的实例
2019/06/25 Python
flask框架自定义过滤器示例【markdown文件读取和展示功能】
2019/11/08 Python
python3 图片 4通道转成3通道 1通道转成3通道 图片压缩实例
2019/12/03 Python
10 套华丽的CSS3 按钮小结
2012/10/03 HTML / CSS
一款纯css3实现的鼠标经过按钮特效教程
2014/11/09 HTML / CSS
京东奢侈品:全球奢侈品牌
2018/03/17 全球购物
致标枪运动员广播稿
2014/02/06 职场文书
信息技术教学反思
2014/02/12 职场文书
运动会演讲稿300字
2014/08/25 职场文书
毕业生实习证明
2014/09/19 职场文书
2016优秀青年志愿者事迹材料
2016/02/25 职场文书
Win11无法访问设备和打印机 如何解决页面空白
2022/04/09 数码科技
Win11控制面板快捷键是什么?Win11打开控制面板的方法汇总
2022/07/07 数码科技
element tree树形组件回显数据问题解决
2022/08/14 Javascript