最少知识原则(LKP)说的是一个软件实体应当尽量少地与其余实体发生相互做用。这
里的软件实体是一个广义的概念,不只包括对象,还包括系统、类、模块、函数、变量等。本
节咱们主要针对对象来讲明这个原则,下面引用《面向对象设计原理与模式》一书中的例子来
解释最少知识原则:
某军队中的将军须要挖掘一些散兵坑。下面是完成任务的一种方式:将军能够通知
上校让他叫来少校,而后让少校找来上尉,并让上尉通知一个军士,最后军士唤来一个
士兵,而后命令士兵挖掘一些散兵坑。
这种方式十分荒谬,不是吗?不过,咱们仍是先来看一下这个过程的等价代码:ajax
gerneral.getColonel( c ).getMajor( m ).getCaptain( c ) .getSergeant( s ).getPrivate( p ).digFoxhole();
让代码经过这么长的消息链才能完成一个任务,这就像让将军经过那么多繁琐的步骤才能命
令别人挖掘散兵坑同样荒谬!并且,这条链中任何一个对象的改动都会影响整条链的结果。
最有可能的是,将军本身根本就不会考虑挖散兵坑这样的细节信息。可是若是将军真的考虑
了这个问题的话,他必定会通知某个军官:“我不关心这个工做如何完成,可是你得命使人去挖
散兵坑。”设计模式
单一职责原则指导咱们把对象划分红较小的粒度,这能够提升对象的可复用性。但愈来愈
多的对象之间可能会产生错综复杂的联系,若是修改了其中一个对象,极可能会影响到跟它相
互引用的其余对象。对象和对象耦合在一块儿,有可能会下降它们的可复用性。在程序中,对象
的“朋友”太多并非一件好事,“城门失火,殃及池鱼”和“一人犯法,株连九族”的故事
时有发生。
最少知识原则要求咱们在设计程序时,应当尽可能减小对象之间的交互。若是两个对象之间不
必彼此直接通讯,那么这两个对象就不要发生直接的相互联系。常见的作法是引入一个第三者对
象,来承担这些对象之间的通讯做用。若是一些对象须要向另外一些对象发起请求,能够经过第三
者对象来转发这些请求。缓存
最少知识原则在设计模式中体现得最多的地方是中介者模式和外观模式,下面咱们分别进行
介绍。闭包
在世界杯期间购买足球彩票,若是没有博彩公司做为中介,上千万的人一块儿计算赔率和输赢
绝对是不可能的事情。博彩公司做为中介,每一个人都只和博彩公司发生关联,博彩公司会根据所
有人的投注状况计算好赔率,彩民们赢了钱就从博彩公司拿,输了钱就赔给博彩公司。
中介者模式很好地体现了最少知识原则。经过增长一个中介者对象,让全部的相关对象都通
过中介者对象来通讯,而不是互相引用。因此,当一个对象发生改变时,只须要通知中介者对象
便可。函数
外观模式,外观模式在 JavaScript中的使用场景并很少。外观模式主要是为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个
接口使子系统更加容易使用.
外观模式的做用是对客户屏蔽一组子系统的复杂性。外观模式对客户提供一个简单易用的高
层接口,高层接口会把客户的请求转发给子系统来完成具体的功能实现。大多数客户均可以经过
请求外观接口来达到访问子系统的目的。但在一段使用了外观模式的程序中,请求外观并非强
制的。若是外观不能知足客户的个性化需求,那么客户也能够选择越过外观来直接访问子系统。
拿全自动洗衣机的一键洗衣按钮举例,这个一键洗衣按钮就是一个外观。若是是老式洗衣机,
客户要手动选择浸泡、洗衣、漂洗、脱水这 4个步骤。若是这种洗衣机被淘汰了,新式洗衣机的
漂洗方式发生了改变,那咱们还得学习新的漂洗方式。而全自动洗衣机的好处很明显,无论洗衣
机内部如何进化,客户要操做的,始终只是一个一键洗衣的按钮。这个按钮就是为一组子系统所
建立的外观。但若是一键洗衣程序设定的默认漂洗时间是 20 分钟,而客户但愿这个漂洗时间是
30分钟,那么客户天然能够选择越过一键洗衣程序,本身手动来控制这些“子系统”运转。
外观模式容易跟普通的封装实现混淆。这二者都封装了一些事物,但外观模式的关键是定义
一个高层接口去封装一组“子系统”。子系统在 C++或者 Java中指的是一组类的集合,这些类相
互协做能够组成系统中一个相对独立的部分。在 JavaScript中咱们一般不会过多地考虑“类”,如
果将外观模式映射到 JavaScript中,这个子系统至少应该指的是一组函数的集合。
最简单的外观模式应该是相似下面的代码:学习
var A = function(){ a1(); a2(); } var B = function(){ b1(); b2(); } var facade = function(){ A(); B(); } facade();
许多 JavaScript设计模式的图书或者文章喜欢把 jQuery的 $.ajax 函数看成外观模式的实现,
这是不合适的。若是 $.ajax 函数属于外观模式,那几乎全部的函数均可以被称为“外观模式”。
问题是咱们根本没有办法越过 $.ajax “外观”去直接使用该函数中的某一段语句。
如今再来看看外观模式和最少知识原则之间的关系。外观模式的做用主要有两点。
为一组子系统提供一个简单便利的访问入口。
隔离客户与复杂子系统之间的联系,客户不用去了解子系统的细节。
从第二点来,外观模式是符合最少知识原则的。好比全自动洗衣机的一键洗衣按钮,隔开了
客户和浸泡、洗衣、漂洗、脱水这些子系统的直接联系,客户不用去了解这些子系统的具体实现。
假设咱们在编写这个老式洗衣机的程序,客户至少要和浸泡、洗衣、漂洗、脱水这 4个子系
统打交道。若是其中的一个子系统发生了改变,那么客户的调用代码就得发生改变。而经过外观
将客户和这些子系统隔开以后,若是修改子系统内部,只要外观不变,就不会影响客户的调用。
一样,对外观的修改也不会影响到子系统,它们能够分别变化而互不影响。es5
封装在很大程度上表达的是数据的隐藏。一个模块或者对象能够将内部的数据或者实现细
节隐藏起来,只暴露必要的接口 API 供外界访问。对象之间不免产生联系,当一个对象必须引
用另一个对象的时候,咱们可让对象只暴露必要的接口,让对象之间的联系限制在最小的
范围以内。
同时,封装也用来限制变量的做用域。在 JavaScript中对变量做用域的规定是:
在es5以前,
1 变量在全局声明,或者在代码的任何位置隐式申明(不用 var ),则该变量在全局可见;
2 变量在函数内显式申明(使用 var ),则在函数内可见。
把变量的可见性限制在一个尽量小的范围内,这个变量对其余不相关模块的影响就越小,
变量被改写和发生冲突的机会也越小。这也是广义的最少知识原则的一种体现。
假设咱们要编写一个具备缓存效果的计算乘积的函数 function mult (){} ,咱们须要一个对
象 var cache = {} 来保存已经计算过的结果。 cache 对象显然只对 mult 有用,把 cache 对象放在
mult 造成的闭包中,显然比把它放在全局做用域更加合适,代码以下:prototype
var mult = (function(){ var cache = {}; return function(){ var args = Array.prototype.join.call( arguments, ',' ); if ( cache[ args ] ){ return cache[ args ]; } var a = 1; for ( var i = 0, l = arguments.length; i < l; i++ ){ a = a * arguments[i]; } return cache[ args ] = a; } })(); mult( 3, 4, 5 ); // 输出: 60
其实,最少知识原则也叫迪米特法则(Law of Demeter,LoD),“迪米特”这个名字源自 1987
年美国东北大学一个名为“Demeter”的研究项目。
许多人更倾向于使用迪米特法则这个名字,也许是由于显得更酷一点。但参考 Head First
Design Patterns的建议,称之为最少知识原则。一是由于这个名字更能体现其含义,另外一个缘由
是“法则”给人的感受是必须强制遵照,而原则只是一种指导,没有哪条原则是在实际开发中必
须遵照的。好比,虽然遵照最小知识原则减小了对象之间的依赖,但也有可能增长一些庞大到难
以维护的第三者对象。跟单一职责原则同样,在实际开发中,是否选择让代码符合最少知识原则,
要根据具体的环境来定。设计