#第七章 设计模式
单体模式
最常见的单体其实就是一个对象字面量: var obj = {"key": "value"};
即使有一个对象字面量拥有相同的属性var obj2 = {"key": "value"};
判断obj === obj2
也会返回false;
还可以通过静态成员或者私有属性来实现单例,即当new 操作执行的时候先判断有没有创建过实例,如果有直接返回。
##工厂模式
工厂模式的目的是为了创建对象。
其最明显的特征是一般不用使用new来创建,而是 XXX.factory(“str”); 一个汽车工厂的demo:
function CarMaker() {} CarMaker.prototype.drive = function () { return "Vroom, I have" + this.doors + "doors"; } CarMaker.factory = function (type) { var constr = type, newcar; if (typeof CarMaker[constr] !== "function") { throw { name: "Error", message: constr + " dosen't extis" } } if (typeof CarMaker[constr].prototype.drive !== "function") { CarMaker[constr].prototype = new CarMaker(); } newcar = new CarMaker[constr](); return newcar; } CarMaker.Compact = function () { this.doors = 4; } var corolla = CarMaker.factory("Compact");
工厂模式在日常开发中的应用场景主要用来创建相似对象,从而避免重复工作。和 普通继承 比起来,普通继承可以继承公用方法,但是构造函数还是需要自己声明。但是工厂模式连构造函数都不需要自己声明。当然它和 继承 是两码事,继承是为了重载某些共有方法,工厂模式可能大部分共有方法都是一样的,只是具有不同的私有属性。
##迭代器模式
迭代器模式其实就是一个复杂的 for 循环,用于遍历某种数据结构,当然它可以比for循环灵活的多,比如可以提供方法从头开始、跳步执行等等。调用的时候通过 Obj.next() 去通知计步器向下:
var agg = (function () { var index = 0, data = [1, 2, 3, 4, 5], length = data.length; return { next: function () { var element; if (!this.hasNext()) { return null; } element = data[index]; index = index + 2; return index; }, hasNext: function () { return index < length; } } }());
##装饰者模式/门面模式
装饰者模式是指在通过从预定义的装饰者对象中添加功能,从而在运行时调整对象。
就是说去动态的更改某个方法以让它适合新的场景。
e.g :
var sale = new Sale(100); //某个售价为100的商品 sale = sale.decorate('fedtax'); //增加联邦税 sale = sale.decorate('rmb'); //转换成rmb sale.getPrice(); // rmb 780
从上述例子可以看出来,装饰者模式可以让某个固定的方法变得非常灵活。比如上述的getPrice();
两种实现方法,第一种方法,把每一种装饰者都当作一个对象,或者说一个中间件,层层继承,先调用rmb对象的getPrice,然后调用父级别,一直往上,拿到最原始的价格。
function Sale(price) { this.price = price || 100; } Sale.prototype.getPrice = function () { return this.price; } Sale.decorators = {}; //fedtax Sale.decorators.fedtax = { getPrice: function () { var price = this.uber.getPrice(); price += price * 5 / 100; return price; } } //rmb Sale.decorators.rmb = { getPrice: function () { var price = this.uber.getPrice(); ... return price; } } //decorators Sale.prototype.decorators = function () { var F = function () {}, overrodes = this.constructor.decorators[decorator], i, newobj; F.prototype = this; newobj = new F(); newobj.uber = F.prototype; for (i in overrides) { if (overrides.hasOwnProperty[i]) { newobj[i] = overrides[i]; } } retutn newobj; }
上述方法是通过原型链的链式调用实现,先方法更简单,通过列表实现:
function Sale(price) { this.price = price || 100; this.decorators_list = []; } Sale.decorators = {}; //fedtax Sale.decorators.fedtax = { getPrice: function (price) { price += price * 5 / 100; return price; } } //rmb Sale.decorators.rmb = { getPrice: function (price) { ... return price; } } //price通过参数 传给不同的门面 //decorate Sale.prototype.decorate = function (decorator) { this.decorators_list.push(decorators); }; Sale.prototype.getPrice = function () { var price = this.price, i, length = this.decorators_list.length, name; for (i = 0;i < max; i += 1) { name = this.decorators_list[i]; price = Sale.decorators[name].getPrice(price); } return price; }
##策略模式
策略模式就像是一个适配器,同一个工作接口,却能根据客户正在试图执行任务的上下文来选择指定算法。
比如解决表单验证,一个validate()方法,无论表单是什么类型,都能支持校验。
var validator = { type: {}, //所有可用的检查方法 config: {}, //当前要检查哪些 message: [],//对应的结果 validate: function (data) { //data: {}要检验的对象,key和config一致 ... for (i in data) { if (data.hasOwnProperty(i)) { type = this.config[i]; checker = this.type[type]; result = cheker.validate(data[i]); } ... } } }##外观模式 外观模式非常简单,就是一种封装,非常常见,把一些不同的业务封装成统一接口,比如:
function stop (e) { e.preventDefault(); e.stopPropagation(); }
##代理模式
代理模式中,一个对象充当另一个对象的接口。一般用作合并某些复杂的开销统一进行,它和外观模式的区别是,外观模式是封装多个方法统一调用为了方便,而代理则是在使用者和提供者之间提供了一层过滤,作为中间者。
书上的例子是一个checkbox,每个checkbox被点击的时候会发一次ajax请求,然后展开,中间放一层代理的好处是可以设置一个延时,比如某个用户快速点击了3个checkbox,传统模式就是需要发送3次ajax请求,而中间加了一层代理,所有的请求由代理发出,代理可以设置比如2s延时,如果2s内有新的勾选,就先等所有勾选全部组合好再去发送ajax。
##中介者模式
中介者模式用于解耦,有些对象之间耦合很紧,一个对象会影响另一个对象,通过一个中介来在他们之间通信,其实和代理模式有点像,代理某种意义上来讲也是一种中介,但是中介应用范围更广,它可以用于状态的维护,书中的例子:
一个按键游戏,玩家一按2,玩家二按0,计分板看谁按得快。
中介者一方面负责接收玩家一和玩家二的消息,另一方面负责维护计分板,还要监听键盘事件等等,而玩家和计分板就被解耦了,从而玩家数量的拓展和计分板的拓展都会很容易,涉及到的改动只有中介者,不会互相影响。
##观察者模式
多用于异步处理。
观察者模式就不多说了,太常见了,所有的dom和外设之间的交互:比如鼠标事件、键盘事件,还要node中的事件模块都大量使用了观察者模式,还有最近很火的promise对象,也是使用了这种模式。
实现原理其实就是维护一个有序队列,在事件触发的时候推送给队列里面的所有订阅者。