大型JavaScript应用程序架构模式(1)(2)
建议的架构
我们本文的重点来了,这次我们提议的架构使用了我们都很熟知的设计模式:module, facade和mediator。
和传统的模型不一样的是,为了解耦各个模块,我们只让模块发布一些event事件,mediator模式可以负责从这些模块上订阅消息message,然后控制通知的response,facade模式用户限制各模块的权限。
以下是我们要注意讲解的部分:
1 设计模式
1.1 模块论
1.1.1 综述
1.1.2 Module模式
1.1.3 对象自面量
1.1.4 CommonJS模块
1.2 Facade模式
1.3 Mediator模式
2 应用到你的架构
2.1 Facade - 核心抽象
2.2 Mediator - 程序核心
2.3 紧密联合运作起来
模块论
大家可能都或多或少地使用了模块化的代码,模块是一个完整的强健程序架构的一部分,每个模块都是为了单独的目的为创建的,回到Gmail,我们来个例子,chat聊天模块看起来是个单独的一部分,其实它是有很多单独的子模块来构成,例如里面的表情模块其实就是单独的子模块,也被用到了发送邮件的窗口上。
另外一个是模块可以动态加载,删除和替换。
在JavaScript里,我们又几种方式来实现模块,大家熟知的是module模式和对象字面量,如果你已经熟悉这些,请忽略此小节,直接跳到CommonJS部分。
Module模式
module模式是一个比较流行的设计模式,它可以通过大括号封装私有的变量,方法,状态的,通过包装这些内容,一般全局的对象不能直接访问,在这个设计模式里,只返回一个API,其它的内容全部被封装成私有的了。
另外,这个模式和自执行的函数表达式比较相似,唯一的不同是module返回的是对象,而自执行函数表达式返回的是function。
众所周知, JavaScript不想其它语言一样有访问修饰符,不能为每个字段或者方法声明private,public修饰符,那这个模式我们是如何实现的呢?那就是return一个对象,里面包括一些公开的方法,这些方法有能力去调用内部的对象。
看一下,下面的代码,这段代码是一个自执行代码,声明里包括了一个全局的对象basketModule, basket数组是一个私有的,所以你的整个程序是不能访问这个私有数组的,同时我们return了一个对象,其内包含了3个方法(例如addItem,getItemCount,getTotal),这3个方法可以访问私有的basket数组。
- var basketModule = (function() {
- var basket = []; //private
- return { //exposed to public
- addItem: function(values) {
- basket.push(values);
- },
- getItemCount: function() {
- return basket.length;
- },
- getTotal: function(){
- var q = this.getItemCount(),p=0;
- while(q--){
- p+= basket[q].price;
- }
- return p;
- }
- }
- }());
同时注意,我们return的对象直接赋值给了basketModule,所以我们可以像下面一样使用:
- //basketModule is an object with properties which can also be methods
- basketModule.addItem({item:'bread',price:0.5});
- basketModule.addItem({item:'butter',price:0.3});
- console.log(basketModule.getItemCount());
- console.log(basketModule.getTotal());
- //however, the following will not work:
- console.log(basketModule.basket);// (undefined as not inside the returned object)
- console.log(basket); //(only exists within the scope of the closure)
那在各个流行的类库(如Dojo, jQuery)里是如何来做呢?
Dojo
Dojo试图使用dojo.declare来提供class风格的声明方式,我们可以利用它来实现Module模式,例如如果你想再store命名空间下声明basket对象,那么可以这么做:
- //traditional way
- var store = window.store || {};
- store.basket = store.basket || {};
- //using dojo.setObject
- dojo.setObject("store.basket.object", (function() {
- var basket = [];
- function privateMethod() {
- console.log(basket);
- }
- return {
- publicMethod: function(){
- privateMethod();
- }
- };
- }()));
结合dojo.provide一起来使用,非常强大。
YUI
下面的代码是YUI原始的实现方式:
- YAHOO.store.basket = function () {
- //"private" variables:
- var myPrivateVar = "I can be accessed only within YAHOO.store.basket .";
- //"private" method:
- var myPrivateMethod = function () {
- YAHOO.log("I can be accessed only from within YAHOO.store.basket");
- }
- return {
- myPublicProperty: "I'm a public property.",
- myPublicMethod: function () {
- YAHOO.log("I'm a public method.");
- //Within basket, I can access "private" vars and methods:
- YAHOO.log(myPrivateVar);
- YAHOO.log(myPrivateMethod());
- //The native scope of myPublicMethod is store so we can
- //access public members using "this":
- YAHOO.log(this.myPublicProperty);
- }
- };
- } ();
jQuery
jQuery里有很多Module模式的实现,我们来看一个不同的例子,一个library函数声明了一个新的library,然后创建该library的时候,在document.ready里自动执行init方法。
- function library(module) {
- $(function() {
- if (module.init) {
- module.init();
- }
- });
- return module;
- }
- var myLibrary = library(function() {
- return {
- init: function() {
- /*implementation*/
- }
- };
- }());






