深入理解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 相关文章推荐
JsEasy简介 JsEasy是什么?与下载
Mar 07 Javascript
fix-ie5.js扩展在IE5下不能使用的几个方法
Aug 20 Javascript
关于jquery append() html时的小问题的解决方法
Dec 16 Javascript
extjs 如何给column 加上提示
Jul 29 Javascript
JavaScript验证知识整理
Mar 24 Javascript
Ionic2调用本地SQlite实例
Apr 22 Javascript
微信小程序 密码输入(源码下载)
Jun 27 Javascript
解决vue里碰到 $refs 的问题的方法
Jul 13 Javascript
微信小程序实现获取自己所处位置的经纬度坐标功能示例
Nov 30 Javascript
JS中验证整数和小数的正则表达式
Oct 08 Javascript
在Vue项目中使用Typescript的实现
Dec 19 Javascript
js实现飞机大战游戏
Aug 26 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
神族 PROTOSS 概述
2020/03/14 星际争霸
使用php实现截取指定长度
2013/08/06 PHP
PHP+Mysql树型结构(无限分类)数据库设计的2种方式实例
2014/07/15 PHP
php通过sort()函数给数组排序的方法
2015/03/18 PHP
PHP中SESSION的注销与清除
2015/04/16 PHP
PHP编程开发怎么提高编程效率 提高PHP编程技术
2015/11/09 PHP
RR vs IO BO3 第一场2.13
2021/03/10 DOTA
各种常用浏览器getBoundingClientRect的解析
2009/05/21 Javascript
Jquery中删除元素的实现代码
2011/12/29 Javascript
jquery交替变换颜色的三种方法 实例代码
2013/11/19 Javascript
jQuery判断元素上是否绑定了指定事件的方法
2015/03/17 Javascript
[原创]Javascript 实现广告后加载 可加载百度谷歌联盟广告
2016/05/11 Javascript
利用jquery正则表达式在页面验证url网址输入是否正确
2017/04/04 jQuery
微信小程序注册60s倒计时功能 使用JS实现注册60s倒计时功能
2017/08/16 Javascript
JavaScript寄生组合式继承实例详解
2018/01/06 Javascript
vue项目实现表单登录页保存账号和密码到cookie功能
2018/08/31 Javascript
一步快速解决微信小程序中textarea层级太高遮挡其他组件
2019/03/04 Javascript
js实现无限瀑布流实例方法
2019/09/16 Javascript
python高并发异步服务器核心库forkcore使用方法
2013/11/26 Python
详解Python中__str__和__repr__方法的区别
2015/04/17 Python
python动态参数用法实例分析
2015/05/25 Python
python如何制作缩略图
2019/04/30 Python
pycharm访问mysql数据库的方法步骤
2019/06/18 Python
python中struct模块之字节型数据的处理方法
2019/08/27 Python
用Python画小女孩放风筝的示例
2019/11/23 Python
python实现二分类和多分类的ROC曲线教程
2020/06/15 Python
美国第二大团购网站:LivingSocial
2016/07/24 全球购物
Booking.com西班牙:全球酒店预订
2018/03/30 全球购物
马德里竞技官方网上商店:Atletico Madrid Shop
2019/03/31 全球购物
上班上网检讨书
2014/01/29 职场文书
《第一次抱母亲》教学反思
2014/04/16 职场文书
超市开店计划书
2014/04/26 职场文书
质量管理标语
2014/06/12 职场文书
高中美术教师事迹材料
2014/08/22 职场文书
公司岗位说明书
2015/10/08 职场文书
2016猴年开门红标语口号
2015/12/26 职场文书