最少知识原则也叫迪米特法则(Law of Demeter,LoD),“迪米特”这个名字源自1987年美国东北大学一个名为“Demeter”的研究项目。

最少知识原则(LKP)说的是一个软件实体应当尽可能少地与其他实体发生相互作用。这里的软件实体是一个广义的概念,不仅包括对象,还包括系统、类、模块、函数、变量等。

如果两个对象之间不必彼此直接通信,那么这两个对象就不要发生直接的相互联系。常见的做法是引入一个第三者对象,来承担这些对象之间的通信作用。如果一些对象需要向另一些对象发起请求,可以通过第三者对象来转发这些请求。

设计模式中的最少知识原则

中介者模式

中介者模式很好的体现了最少知识原则。通过增加一个中介者对象,让所有的相关对象都通过中介者对象来通信,而不是互相引用。

外观模式

外观模式主要是为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使子系统更加容易使用。

外观模式

外观模式的作用是对客户屏蔽一组子系统的复杂性。外观模式对客户提供一个简单易用的高层接口,高层接口会把客户的请求转发给子系统来完成具体的功能实现。大多数客户都可以通过请求外观接口来达到访问子系统的目的。但在一段使用了外观模式的程序中,请求外观并不是强制的。如果外观不能满足客户的个性化需求,那么客户也可以选择越过外观直接访问子系统。

外观模式容易跟普通的封装实现混淆。这两者都封装了一些事物,但外观的关键是定义一个高层接口去封装一组“子系统”。“子系统”可以理解为一组函数的集合。

var A = function(){ 
    a1();
    a2(); 
}
var B = function(){ 
    b1();
    b2(); 
}
var facade = function(){ 
    A();
    B(); 
}
facade();

外观模式和最少知识原则之间的关系

  • 为一组子系统提供一个简单便利的访问入口
  • 隔离客户与复杂子系统之间的联系,客户不用去了解子系统的细节

封装在最少知识原则中的体现

封装在很大程度上表达的是数据的隐藏。一个模块或者对象可以将内部的数据或者实现细节隐藏起来,只暴露必要的接口API 供外界访问。对象之间难免产生联系,当一个对象必须引用另外一个对象的时候,我们可以让对象只暴露必要的接口,让对象之间的联系限制在最小的范围之中。

封装也用来限制变量的作用域。变量作用域的规定:

  • 变量在全局声明,或者在代码的任何位置隐士声明(不用var),则该变量在全局可见;
  • 变量在函数内声明(使用var),则在函数内可见。

如果我们要编写一个具有缓存效果的计算乘积的函数function mult(){},我们需要一个对象var cache={}来保存已经计算过的结果。cache对象显然只对mult有用,把cache对象放在mult形成的闭包中,显然比把它放在全局作用域更加合适

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];
        }
    }
})();
mult(1,2,3); // 输出:6

虽然遵守最小知识原则减少了对象之间的依赖,但也有可能增加一些庞大到难以维护的第三者对象。跟单一职责原则一样,在实际开发中,是否选择让代码符合最少知识原则,要根据具体的环境来定。