javascript设计模式-外观模式

  1 <!DOCTYPE HTML>

  2 <html lang="en-US">

  3 <head>

  4     <meta charset="utf-8">

  5     <title></title>

  6 </head>

  7 <body>

  8 

  9 <script>

 10     /**

 11      * 门面模式

 12      *

 13      * 定义:

 14      * 为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

 15      *

 16      * 本质:

 17      * 封装交互,简化调用

 18      *

 19      * 门面模式有两个作用:一是简化类的接口;二是消除类与使用它的客户代码之间的耦合。在js中,门面模式常常是开发人员最亲密的朋友。它是几乎所有js库的核心原则。通过创建一些便利方法让复杂系统变得更加简单易用,门面模式可以使库提供的工具更容易理解。使用这种模式,程序员可以间接地与一个子系统打交道,与直接访问子系统相比,这样做更不容易出错。

 20      * 门面模式简化了诸如错误记录或跟踪页面视图统计数据这类常用的或重复性的任务。通过添加一些便利方法(这种方法是对原有的一些方法的组合利用),它还可以让对象的功能显得更加完善。

 21      * 门面模式可用于简化复杂接口。它可以在幕后为你进行错误检查,清除不再需要的大对象,以及用一种更加易用的方式展现对象的功能。

 22      * 门面模式并非必不可少,同样的任务不用它也能完成。这是一种组织性的模式,它可以用来修改类和对象的接口,使其更便于使用。它可以让程序员过的更轻松,使代码变得更容易管理。

 23      * 这个模式在DOM脚本编程这种需要面对各种不一致的浏览器接口的环境中很常用。

 24      *

 25      * 外观模式的目的:

 26      * 外观模式的目的不是给子系统添加新的功能接口,而是为了让外部减少与子系统内多个模块的交互,松散耦合,从而让外部能够更简单地使用子系统。

 27      * 这点要特别注意,因为外观是当作子系统对外的接口出现的,虽然也可以在这里定义依稀子系统没有的功能,但不建议这么做,外观应该是包装已有的功能,它主要负责组合已有功能来实现客户需要,而不是添加新的实现。

 28      *

 29      * 表面上看就是把客户端的代码搬到Facade里面了。

 30      *

 31      * 对设计原则的体现

 32      * 很好的体现了“最少知识原则”

 33      *

 34      */

 35     

 36     /*

 37     界面:

 38     主要指的是从一个组件外部来看这个组件,,能够看到什么,就、这就是这个组件的界面,也就是所说的外观,

 39     比如,你从一个类外部来看这个类,那么这个类的public方法就是这个类的外观。

 40     再比如,你从一个模块外观来看这个模块,那么这个模块对外的接口就是这个模块的外观,因为你只能看到这些接口,其他的模块内部实现的部分是被接口封装隔离的。

 41 

 42     接口:

 43     主要指的是外部和内部交互的一个通道,通常是指一些方法,可以是类的方法,也可以是interface方法。

 44      */

 45 

 46     /*

 47      基于GUI的操作系统就是计算机上的数据和功能的一个门面。每次点击,拖动和移动某个东西时,实际上是在跟一个门面打交道,间接地执行了一些幕后命令。

 48      */

 49 

 50     // 跨浏览器事件侦听器

 51     function addEvent(el, type, fn) {

 52         if (window.addEventListener) {

 53             el.addEventListener(type, fn, false);

 54         } else if (window.attachEvent) {

 55             el.attachEvent('on' + type, fn);

 56         } else {

 57             el['on' + type] = fn;

 58         }

 59     }

 60     /*

 61      addEvent函数是一个基本的门面,有了它,就有了一种为DOM节点添加事件侦听器的简便方法。

 62      */

 63 

 64     // 用作便利方法的门面元素

 65     /* 门面模式给予开发人员的另一个好处表现在对函数的组合上。这些组合而得的函数又叫便利函数(convenience function).

 66      */

 67     function a(x) {

 68         // do stuff here...

 69     }

 70     function b(y) {

 71         // do stuff here...

 72     }

 73     function ab(x,y) {

 74         a(x);

 75         b(y);

 76     }

 77 

 78     // DOM中的例子

 79     var DED=window.DED || {};

 80     DED.util={

 81         stopPropagation:function(e){

 82             if(e.stopPropagation) {

 83                 // w3 interface

 84                 e.stopPropagation();

 85             } else {

 86                 // IE's interface

 87                 e.cancelBubble=true;

 88             }

 89         },

 90         preventDefault:function(e){

 91             if(e.preventDefault) {

 92                 e.preventDefault();

 93             } else {

 94                 e.returnValue=false;

 95             }

 96         },

 97         // our convenience method

 98         stopEvent:function(e){

 99             DED.util.stopPropagation(e);

100             DED.util.preventDefault(e);

101         }

102     };

103 

104 

105     // 示例: 设置HTML元素的样式

106     function setStyle(elements,prop,val) {

107         for(var i= 0,len=elements.length;i<len;i++) {

108             document.getELementById(elements[i]).style[prop]=val;

109         }

110     }

111     setStyle(['foo'], 'position', 'absolute');

112 

113     function setCSS(el,styles){

114         for(var prop in styls) {

115             if(!styls.hasOwnProperty(prop)) {

116                 continue;

117             }

118             setStyle(el,prop,styls[prop]);

119         }

120     }

121     setCSS(['foo', 'bar'], {

122         position:'absolute',

123         top:'50px',

124         left:'300px'

125     });

126 

127 

128     // 示例: 设计一个事件工具

129     DED.util.Event={

130         getEvent:function(e){

131             return e || window.event;

132         },

133         getTarget:function(e){

134             e = this.getEvent(e);

135             return e.target || e.srcElement;

136         },

137         stopPropagation:function(e){

138             e = this.getEvent(e);

139             if(e.stopPropagation) {

140                 e.stopPropagation();

141             } else {

142                 e.cancelBubble=true;

143             }

144         },

145         preventDefault:function(e){

146             e = this.getEvent(e);

147             if(e.preventDefault) {

148                 e.preventDefault();

149             } else {

150                 e.returnValue=false;

151             }

152         },

153         stopEvent:function(e){

154             this.stopPropagation(e);

155             this.preventDefault(e);

156         }

157     };

158 

159     addEvent($('example'), 'click', function(e){

160         console.log(DED.util.Event.getTarget(e));

161         DED.util.Event.stopEvent(e);

162     });

163 

164     /*

165      实现门面模式的一般步骤

166      找准自己的应用程序中感觉适合使用门面方法的地方后,就可以着手加入便利方法了。这些函数的名称应该仔细考虑,与它们的用途要相称。对于有几种函数组合而成的函数,一个简单的办法就是把相关函数的名称串连成一个函数名,并采用camel大写规范。

167      处理浏览器API的不一致性属于另一种情况,此时要做的就是把分支代码放在新创建的门面函数中,辅以对象检查或浏览器嗅探等技术。命名方式应该以易识别为主。

168 

169      门面模式的适用场合

170      判断是否应该应用门面模式的关键在于辨认那些反复成组出现的代码。如果函数b出现在函数a之后这种情况经常出现,那么也许你应该考虑添加一个把这两个函数组合起来的门面函数。

171      在自己的核心工具代码中加入门面函数的另一个可能目的是应对js内置函数在不同浏览器中的不同表现。

172      */

173 

174     /*

175      门面模式之利

176      使用门面模式的目的就是要让程序员过的更轻松一些。编写一次组合代码,然后就可以反复使用它,这有助于节省时间和精力,他们提供了一个处理常见问题和任务的简化接口。

177      门面方法方便了开发人员,并且提供了较高层的功能,他们还能降低对外部代码的依赖程度。为应用系统的开发增加了一些额外的灵活性。通过使用门面模式,可以避免与下层子系统紧密耦合。这样就可以对这个系统进行修改该而不会影响到客户代码。

178 

179      门面模式之弊

180      有时候门面元素也会带来一些不必要的额外负担。门面模式时常常会被滥用,导致小题大做。在实施一些套路之前应该认真掂量一下其实用性,因为它们不易察觉的破坏性和昂贵代价可能会是应用程序步履蹒跚。

181      对于简单的个人网站或少量营销网页来说,仅为工具提示和弹出式窗口这样一点增强行为就导入整个js库可能并不明智。

182      */

183     

184     /***

185     外观模式的实现-----------------------------------

186     */

187 

188     /*

189     1.Facade的实现

190     对于一个子系统而言,外观类不需要很多,通常可以实现成为一个单例。也可以直接把外观中的方法实现称为静态的方法,这样就可以不需要创建外观对象的实例而直接调用,这种实现相当于把外观类当成一个辅助工具类实现。

191      */

192     

193      var Facade = function(){};

194      Facade.prototype = {

195         test: function(){

196             var a = new AModuleImpl();

197             a.testA();

198             var b = new BModuleImpl();

199             b.testB();

200             var c = new CModuleImpl();

201             c.testC();

202         }

203      };

204 

205      /*

206      2.Facade可以实现成为Interface

207      虽然Facade通常直接实现成为类,但是也可以把Facade实现成为真正的interface,只是这样会增加系统的复杂度,因为这样会需要一个Facaded的实现,还需要一个来获取Facade接口对象的工厂。

208 

209      3.Facade实现成为interface的附带好处

210      如果把Facade实现成为接口,就能够有选择性地暴露接口的方法,尽量减少模块对子系统外提供的接口方法。

211 

212      换句话说,一个模块的接口中定义的方法可以分成两部分,一部分是给子系统外部使用的,一部分是子系统内部的模块间相互调用时使用的。有了Facade接口,那么用于仔细用内部的接口功能就不用暴露给子系统的外部了。

213       */

214      

215      var AModuleApi = function(){};

216      AModuleApi.prototype = {

217         // 对子系统外部

218         a1: function(){},

219         // 其他方法是用在子系统内部,与B,C模块交互用

220         a2: function(){},

221         a3: function(){}

222      };

223 

224      var FacadeApi = {

225         // 这些是A,B,C模块对仔细用外的接口,这样外部就不需要知道A,B,C模块的存在,

226         // 只需要知道Facade接口就行了。

227         a1: function(){},

228         b1: function(){},

229         c1: function(){},

230 

231         // 这是对外提供的组合方法

232         test: function(){}

233      };

234 

235      /*

236      4.Facade的方法实现

237      Facade的方法实现中,一般是负责把客户端的请求转发给子系统内部的各个模块进行处理,Facade本身并不进行功能的处理。Facade的方法实现只是实现一个功能的组合调用。

238       */

239      

240      /*

241      相关模式

242 

243      外观模式和中介者模式

244 

245      这两个模式非常类似,但是却有本质的区别。

246      中介者模式主要用来封装多个对象之间相互的交互,多用在系统内部的多个模块之间;而外观模式封装的是单向的交互,是从客户端访问系统的调用,没有从系统中来访问客户端的调用。

247      在中介者模式的实现里面,是需要实现具体的交互功能的;而外观模式的实现里面,一般是组合调用或是转调内部实现的功能,通常外观模式本身并不实现这些功能。

248      中介者模式的目的主要是松散多个模块之间的耦合,把这些耦合关系全部放到中介者中去实现;而外观模式的默读是简化客户端的调用,这点和中介者模式也不同。

249 

250      外观模式和单例模式

251 

252      通常一个子系统只需要一个外观实例,所以外观模式可以和单例模式组合使用,吧Facade类实现成为单例。当然,要把外观类的构造器私有化,然后把提供给客户端的方法实现成为静态的。

253 

254      外观模式和抽象工厂模式

255      外观模式的外观类通常需要和系统内部的多个模块交互,每个模块一般都有自己的接口,所以在外观类的具体实现里面,需要获取这些接口,然后组合这些接口来完成客户端的功能。

256      那么怎么获取这些接口呢?就可以和抽象工厂一起使用,外观类通过抽象工厂来获取所需要的接口,而抽象工厂也可以把模块内部的实例对Facade进行屏蔽,也就是说Facade也仅仅只是知道它从模块中获取它需要的功能,模块内部的细节,Facade也不知道。

257       */

258      

259 

260       // http://www.dofactory.com/javascript-facade-pattern.aspx

261 

262     var Mortgage = function(name) {

263         this.name = name;

264     }

265 

266     Mortgage.prototype = {

267         applyFor: function(amount) {

268 

269             // access multiple subsystems...

270 

271             var result = "approved";

272             if (!new Bank().verify(this.name, amount)) {

273                 result = "denied";

274             } else if (!new Credit().get(this.name)) {

275                 result = "denied";

276             } else if (!new Background().check(this.name)) {

277                 result = "denied";

278             }

279 

280             return this.name + " has been " + result +

281                    " for a " + amount + " mortgage";

282         }

283     }

284 

285     var Bank = function() {

286         this.verify = function(name, amount) {

287             // complex logic ...

288             return true;

289         }

290     }

291     var Credit = function() {

292         this.get = function(name) {

293             // complex logic ...

294             return true;

295         }

296     }

297     var Background = function() {

298         this.check = function(name) {

299             // complex logic ...

300             return true;

301         }

302     }

303 

304 

305     function run() {

306 

307         var mortgage = new Mortgage("Joan Templeton");

308         var result = mortgage.applyFor("$100,000");

309 

310         alert(result);

311     }

312 

313 </script>

314 </body>

315 </html>

 

你可能感兴趣的:(JavaScript)