设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。 使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
要学会设计模式,首先要了解设计模式所依托的设计原则:
(1)单一职责原则(SRP原则):一个对象(方法)只做一件事情。
运用的设计模式:代理模式,单例模式,装饰者模式等。
(2)最少知识原则(LKP原则):一个软件实体应当尽可能少地与其他实体发生相互作用。软件实体是一个广义的概念,不仅包括对象,还包括系统,类,模块,函数,变量等。
最少知识原则要求我们在设计程序时,应当尽量减少对象之间的交互,一个对象应尽可能少的了解其他对象。
运用的设计模式:中介者模式,外观模式等
(3)开闭原则(开放-封闭原则/OCP原则):软件实体(类,模块,函数)等应该是可以扩展的,但是不可修改。
运用的设计模式:发布-订阅模式,模板方法模式,策略模式,代理模式,职责链模式等。
(4)里氏转换原则:子类继承父类,单独掉完全可以
(5)依赖倒转原则:引用一个对象,如果它有底层对象,直接调用底层。
(6)合成/聚合复用原则:新的对象应尽可能使用一些已有的对象,使之成为新对象的一部分
(7) 接口隔离原则。不应该强迫客户依赖没有使用的接口。有个问题是,JS中没有显式的接口,不过我们有方法绕过。
SOLID设计原则请参考: http://www.infoq.com/cn/news/2014/01/solid-principles-javascript
(1)单体(Singleton)模式(单例模式): 绝对是JavaScript中最基本最有用的模式。
单例概念:保证一个类只有一个实例
实现的方法:先判断实例存在与否,存在直接返回,不存在就创建了再返回,这确保一个类只有一个实例对象。
用途:
a.用来划分命名空间,从全局命名空间里提供一个唯一的访问点来访问该对象。
b.实现模块内部保护,模块之间通信
优点:可以减少网页中全局变量的数量(在网页中使用全局变量有风险);可以在多人开发时避免代码的冲突(使用合理的命名空间)等。
使用注意事项:
a.this的使用
b.闭包容易造成内存泄漏,不需要赶快处理掉
c.注意new的成本(继承)
实例:
<script>
//top模块(与下面的banner模块是独立的命名空间,互不干扰(自己保护自己),相互可以通信)
var top = {
init: function () {
//先绑定dom元素
this.render();
//再为dom元素绑定事件
this.bind();
},
a: 4, //想传递的量
//render 把所有的dom元素放在这里边
render: function () {
var me = this;
me.btn_a = $('#a');
},
//bind 给元素绑定事件
bind: function(){
var me = this;
me.btn_a.click(function(){
//业务逻辑,定义在下边
me.test();
});
},
test: function(){
a = 5;
}
};
//banner模块
var banner = {
init: function () {
//先绑定dom元素
this.render();
//再为dom元素绑定事件
this.bind();
},
a: 4, //想传递的量
//render 把所有的dom元素放在这里边
render: function () {
var me = this;
me.btn_a = $('#a');
},
//bind 给元素绑定事件
bind: function(){
var me = this;
me.btn_a.click(function(){
//业务逻辑,定义在下边
me.test();
});
},
test: function(){
//a = 6;
top.a = 6;//直接调用模块 top. ... 实现和top的通信
}
};
//初始化各模块
top.init();
banner.init();
</script>
详细请参考:http://www.cnblogs.com/TomXu/archive/2012/02/20/2352817.html
(2)工厂模式
概念:定义一个用于创建对象的接口,该接口由子类决定实例化哪一个类。该模式使一个类的实例化延迟到了子类。而子类可以重写接口方法以便创建的时候指定自己的对象类型。
用途:
适用于:
a.对象的构建较复杂时的场景
b.需要依赖具体的环境创建不同实例。
c.处理大量具有相同属性的小对象。
注意事项:
a.不要滥用工厂模式,如果只是为了生产很好的几个对象,创建工厂只会增加代码复杂度。(会生产大量小对象才适合建工厂流水线)
优点:消除对象之间的耦合(何为耦合?就是相互影响)。通过使用工厂方法而不是new关键字及具体类,可以把所有实例化的代码都集中在一个位置,有助于创建模块化的代码
<script>
//工厂是一个单体(单例模块)
var myFactory = {};
//不同的工厂流水线
myFactory.product_shoes = function(){
this.workers = 100;
alert('生产鞋子');
};
myFactory.product_clothes = function(){
this.workers = 500;
alert('生产衣服');
};
//定义一个类,它的实例化延迟到了下面的子类里
myFactory.manager = function(para){
return new myFactory[para]();
}
//子类,进行实例化,决定生产哪种和哪样的东西
var somebody = myFactory.manager('product_shoes');
//alert(somebody.workers);
</script>
另一个非常有名的一个示例 - XHR工厂:
var XMLHttpFactory =function(){}; //这是一个简单工厂模式
XMLHttpFactory.createXMLHttp =function(){
var XMLHttp = null;
if (window.XMLHttpRequest){
XMLHttp = new XMLHttpRequest()
}elseif (window.ActiveXObject){
XMLHttp = new ActiveXObject("Microsoft.XMLHTTP")
}
return XMLHttp;
}
//XMLHttpFactory.createXMLHttp()这个方法根据当前环境的具体情况返回一个XHR对象。
var AjaxHander =function(){
var XMLHttp = XMLHttpFactory.createXMLHttp();
...
}
工厂有简单工厂,抽象工厂之分:
var XMLHttpFactory =function(){}; //这是一个抽象工厂模式
XMLHttpFactory.prototype = {
//如果真的要调用这个方法会抛出一个错误,它不能被实例化,只能用来派生子类
createFactory:function(){
thrownew Error('This is an abstract class');
}
}
//派生子类,文章开始处有基础介绍那有讲解继承的模式,不明白可以去参考原理
var XHRHandler =function(){
XMLHttpFactory.call(this);
};
XHRHandler.prototype =new XMLHttpFactory();
XHRHandler.prototype.constructor = XHRHandler;
//重新定义createFactory 方法
XHRHandler.prototype.createFactory =function(){
var XMLHttp =null;
if (window.XMLHttpRequest){
XMLHttp =new XMLHttpRequest()
}elseif (window.ActiveXObject){
XMLHttp =new ActiveXObject("Microsoft.XMLHTTP")
}
return XMLHttp;
}
区别:
简单工厂 : 用来生产同一等级结构中的任意产品。(对于增加新的产品,无能为力)
工厂方法 :用来生产同一等级结构中的固定产品。(支持增加任意产品)
抽象工厂 :用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族)
以上三种工厂 方法在等级结构和产品族这两个方向上的支持程度不同。所以要根据情况考虑应该使用哪种方法。
简单工厂优点:客户端可以免除直接创建产品对象的责任,而仅仅是“消费”产品。简单工厂模式通过这种做法实现了对责任的分割。
工厂方法有点:允许系统在不修改具体工厂角色的情况下引进新产品。
抽象工厂优点:向客户端提供一个接口,使得客户端在不必指定产品具体类型的情况下,创建多个产品族中的产品对象
抽象工厂简单工厂等的更详细区别请见:http://zyjustin9.iteye.com/blog/2094960
(3)桥接模式
概念:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
用途:最常用在事件监控上。在实现API的时候,桥接模式非常有用。在所有模式中,这种模式最容易立即付诸实施。
优点:可以用来弱化它与使用它的类和对象之间的耦合,就是将抽象与其实现隔离开来,以便二者独立变化;这种模式对于JavaScript中常见的时间驱动的编程有很大益处。桥接模式让API更加健壮,提高组件的模块化程度,促成更简洁的实现,并提高抽象的灵活性。
//错误的方式
//这个API根据事件监听器回调函数的工作机制,事件对象被作为参数传递给这个函数。本例中并没有使用这个参数,而只是从this对象获取ID。
addEvent(element,'click',getBeerById);
function(e){
var id =this.id;
asyncRequest('GET','beer.url?id='+ id,function(resp){
//Callback response
console.log('Requested Beer: '+ resp.responseText);
});
}
//好的方式
//从逻辑上分析,把id传给getBeerById函数式合情理的,且回应结果总是通过一个回调函数返回。这么理解,我们现在做的是针对接口而不是实现进行编程,用桥梁模式把抽象隔离开来。
function getBeerById(id,callback){
asyncRequest('GET','beer.url?id='+ id,function(resp){
callback(resp.responseText)
});
}
addEvent(element,'click',getBeerByIdBridge);
function getBeerByIdBridge(e){
getBeerById(this.id,function(beer){
console.log('Requested Beer: '+ beer);
});
}
(4)装饰者(Decorator)模式
目的:为对象增加功能(或方法)。
概念:动态地给一个对象添加一些额外的职责。就扩展功能而言,它比生成子类方式更为灵活。
装饰者模式和组合模式有很多共同点,它们都与所包装的对象实现统一的接口并且会把任何方法条用传递给这些对象。可是组合模式用于把众多子对象组织为一个整体,而装饰者模式用于在不修改现有对象或从派生子类的前提下为其添加方法。
装饰者的运作过程是透明的,这就是说你可以用它包装其他对象,然后继续按之前使用那么对象的方法来使用。
(5)组合(Composite)模式
概念:将对象组合成树形结构以表示“部分-整体”的层次结构。它使得客户对单个对象和复合对象的使用具有一致性。
组合模式是一种专为创建Web上的动态用户界面而量身定制的模式。使用这种模式,可以用一条命令在多个对象上激发复杂的或递归的行为。组合模式擅长于对大批对象进行操作。
优点:1.程序员可以用同样的方法处理对象的集合与其中的特定子对象;2.它可以用来把一批子对象组织成树形结构,并且使整棵树都可被便利。
适用范围:1.存在一批组织成某处层次体系的对象(具体结构可能在开发期间无法知道);2.希望对这批对象或其中的一部分对象实话一个操作。
其实组合模式就是将一系列相似或相近的对象组合在一个大的对象,由这个大对象提供一些常用的接口来对这些小对象进行操作,代码可重用,对外操作简单。例如:对form内的元素,不考虑页面设计的情况下,一般就剩下input了,对于这些input都有name和value的属性,因此可以将这些input元素作为form对象的成员组合起来,form对象提供对外的接口,便可以实现一些简单的操作,比如设置某个input的value,添加/删除某个input等等。
(6)门面(facade)模式
概念:
门面模式是几乎所有JavaScript库的核心原则
子系统中的一组接口提供一个一致的界面,门面模式定义了一个高层接口,这个接口使得这一子系统更加容易使用,简单的说这是一种组织性的模式,它可以用来修改类和对象的接口,使其更便于使用。
作用:1.简化类的接口;2.消除类与使用它的客户代码之间的耦合。
比如计算机桌面上的那些快捷方式图标,它们就是在扮演一个把用户引导至某个地方的接口的角色,每次操作都是间接的执行一些幕后的命令。
还有很多设计模式的详细介绍请参考tom大叔的深入理解javascript系列博客,哪里对很多设计模式都进行了很详细的讲解:
http://www.cnblogs.com/TomXu/category/338104.html