大熊君说说JS与设计模式之(门面模式Facade)迪米特法则的救赎篇------(监狱的故事)
一,总体概要
1,笔者浅谈
说起“门面”这个设计模式其实不论新老程序猿都是在无意中就已经运用到此模式了,就像我们美丽的JS程序员一样不经意就使用了闭包处理问题,
1 function Employee(name) { 2 var name = name; 3 this.say = function () { 4 console.log("I am employee " + name) ; 5 } ; 6 }
代码中“say”是一个function,Employee也是一个function这就构成了一个闭包环境,其实多数我们都是如上的定义方式,也就潜移默化的使用了闭包,O(∩_∩)O~ 让我们继续回来,门面这个模式很容易理解见如下图
图中描述的很清晰,三个子系统彼此独立有自己的职责划分,从客户端的角度来看是不关心它们细节的,那么如何把这三个子系统与客户端做连线那?好的!!!这时文中的主角该亮相了,这就是门面模式它起到了一个统一入口的职责,
降低子系统之间客户端与实现化层之间的依赖性。当在构建一个层次化的系统时,也可以同过使用Facade模式定义系统中每一层的入口,从而简化层与层之 间的依赖关系。
文字可能会有些抽象但是慢慢来,一步步分析,现在假设有这么个场景,有两个以完善的插件模块,基于这个再做二次开发,我们想想这就和图中的场景很类似了,以下是模拟代码有助于理解
1 function HelloWorld(){ 2 this.greetPlug = new GreetPlug() ; 3 this.filterPlug = new FilterPlug() ; 4 this.hello = function(name){ 5 if(!this.filterPlug.filter(name,"zhangsan")){ 6 this.greetPlug().say(name) ; 7 } 8 } 9 } ; 10 function GreetPlug(){ 11 this.say = function(what){ 12 console.log("Hello," + what) ; 13 } 14 } ; 15 function FilterPlug(){ 16 this.filter = function(what,not){ 17 return (what === not) ? true : false ; 18 } 19 } ;
里面的HelloEorld就是相对于和门面接口,至于两外两个类是隐藏的,这就是大概的思路!再看两个应用例子加深理解,如下图
隐藏了DOM标准事件接口的细节,做了事件兼容的api。
这个大家就更熟悉了,jQuery的Ajax操作是大家平时操作相对来说比较频繁的,没错它也用到了门面模式,底层是低级api做支持,门面层通过不同参数划分职能!
二,门面与迪米特法则
迪米特法则(Law of Demeter)又叫作最少知识原则(Least Knowledge Principle 简写LKP),就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。英文简写为: LoD.
迪米特法则的核心观念就是类间解耦,弱耦合,只有弱耦合了以后,类的复用性才可以提高。
其实二者的关系很好理解,门面模式就是基于此法则衍生出来的解决方案,适用于特定场景的同一类问题的解决方案。下面再来看个例子,如下
现在有这么个场景------监狱内的犯人是不应该跟外面的人接触的,当然或许会有探亲的。这里的监狱就是类,里面的犯人就是类内部的信息,而监狱里的狱警就相当于迪米特法则的执行者
1 function Inmates(){ 2 this.say = function(){ 3 console.log('狱友说:我们是狱友...") ; 4 } ; 5 } ; 6 function Prisoners(){ 7 this.inmates = new Inmates() ; 8 this.helpEachOther = function(){ 9 this.inmates.say() ; 10 console.log("家人说:你和狱友之间应该互相帮助...") ; 11 } ; 12 } ; 13 function Family(){ 14 this.prisoners = new Prisoners() ; 15 this.visit = function(inmates,prisoners){ 16 this.prisoners.helpEachOther() ; 17 } ; 18 } ;
看到这样的结果,是不是有些别扭,家人告诉犯人要与狱友好好相处,而狱友确冒出来说话。这显然越界了,因为监狱只允许家人探望犯人,而不是随便谁都可以见的
这里的家人和狱友有了沟通是违背迪米特法则的,所以我们需要将家人和狱友隔离开,对其进行重构
(1),定义家人类
1 function Family(){ 2 this.prisoners = new Prisoners() ; 3 this.visit = function(prisoners){ 4 console.log("家人说:") ; 5 this.prisoners.helpEachOther() ; 6 } ; 7 } ;
(2),定义犯人类
1 function Prisoners(){ 2 this.inmates = new Inmates() ; 3 this.helpEachOther = function(){ 4 console.log("犯人和狱友之间应该互相帮助...") ; 5 console.log("犯人说:") ; 6 inmates.weAreFriends() ; 7 } ; 8 } ;
(3),定义狱友类
1 function Inmates(){ 2 this.weAreFriends = function(){ 3 console.log('我们是狱友...") ; 4 } ; 5 } ;
(4),定义场景类
1 function Prison(){ 2 new Family().visit(new Prisoners()) ; 3 } ;
这样家人和狱友就分开了,但是也表达了家人希望狱友能跟犯人互相帮助的意愿。也就是两个类通过第三个类实现信息传递
三,概要总结
迪米特法则的总结:
迪米特法则不希望类直接建立直接的接触。如果真的有需要建立联系,也希望能通过它的友元类来转达。因此,应用迪米特法则有可能造成的一个后果就是:系统中存在大量的中介类,
这些类之所以存在完全是为了传递类之间的相互调用关系------这在一定程度上增加了系统的复杂度。有兴趣可以研究一下设计模式的门面模式(Facade)和中介模式(Mediator),
都是迪米特法则应用的例子。
门面模式的优点:
松散耦合
门面模式松散了客户端与子系统的耦合关系,让子系统内部的模块能更容易扩展和维护。
简单易用
门面模式让子系统更加易用,客户端不再需要了解子系统内部的实现,也不需要跟众多子系统内部的模块进行交互,只需要跟门面类交互就可以了。
更好的划分访问层次
通过合理使用Facade,可以帮助我们更好地划分访问的层次。有些方法是对系统外的,有些方法是系统内部使用的。把需要暴露给外部的功能集中到门面中,这样既方便客户端使用,也很好地隐藏了内部的细节。
哈哈哈,本篇结束,未完待续,希望和大家多多交流够沟通,共同进步(*^__^*) 嘻嘻……