什么是设计模式
设计模式是由代码结构优化经验萃取出来的理论知识,应用成熟的设计模式能够增强代码的可复用性、可扩展性与可维护性。
设计模式不能滥用。
常用设计模式
一、策略模式
1. 定义
定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
即把可以替换的算法步骤封装成一个个算法族,供运行时动态选择
2. 核心
- 策略模式核心是对算法的封装
- 专注于实现算法(策略)的选择,支持运行时动态改变策略
- 具体实现是把变化的部分找出来,定义为接口,每个接口对应一组算法,每一个都是一种策略
- 同一接口下的算法是可以相互替换的
- 算法是独立于客户代码的,也就是对算法封装的具体体现
一个基于策略模式的程序至少由两部分组成:
第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。
第二个部分是环境类Context,Context接受客户的请求,随后把请求委托给某一个策略类。要做到这点,说明Context 中要维持对某个策略对象的引用
3. 举例
假设需要通过成绩等级来计算学生的最终得分,每个成绩等级有对应的加权值。我们可以利用对象字面量的形式直接定义这个组策略
// 加权映射关系
var levelMap = {
S: 10,
A: 8,
B: 6,
C: 4
};
// 组策略
var scoreLevel = {
basicScore: 80,
S: function() {
return this.basicScore + levelMap['S'];
},
A: function() {
return this.basicScore + levelMap['A'];
},
B: function() {
return this.basicScore + levelMap['B'];
},
C: function() {
return this.basicScore + levelMap['C'];
}
}
// 调用
function getScore(level) {
return scoreLevel[level] ? scoreLevel[level]() : 0;
}
console.log(
getScore('S'),
getScore('A'),
getScore('B'),
getScore('C'),
getScore('D')
); // 90 88 86 84 0
4. 优缺点
优点
可以有效地避免多重条件语句,将一系列方法封装起来也更直观,利于维护
缺点
往往策略集会比较多,我们需要事先就了解定义好所有的情况
二、 观察者模式
1. 定义
定义并维护对象之间的一对多关系
把现实世界中的报纸与订阅者的关系抽象出来就是观察者模式,一种报纸对应多个订阅者,订阅者可以随时解除订阅,未订阅的读者也可以随时开始订阅。一旦有新报纸发布,所有的订阅者都会收到新内容。
在观察者模式中,报纸叫做主题Subject,订阅者叫做观察者Observer,一个Subject可以被多个Observer关注,Observer可以随时解除关注,新的Observer也可以随时关注Subject。Subject内容发生改变时,会通知所有的Observer。
注意:有些人认为观察者模式就是发布订阅模式,但实际上观察者模式和发布订阅模式是有区别的。
区别:观察者模式只有两个,一个是观察者一个是被观察者。发布订阅模式不一样,发布订阅模式还有一个中间层,发布订阅模式的实现是,发布者通知给中间层 => 中层接受并通知订阅者 => 订阅者收到通知并发生变化
2. 核心
-
利用观察者模式可以轻易地建立对象之间“一对多”的依赖关系
-
利用观察者模式的机制可以很容易的实现这种依赖关系的动态维护
3. 举例
举一个简单的例子:我们曾经在DOM节点上面绑定过事件函数,那我们就使用过观察者模式,因为JS和DOM之间就是实现了一种观察者模式。
document.body.addEventListener("click", function() {
alert("Hello World")
},false )
document.body.click() //模拟用户点击
以上js就是观察者,DOM就是被观察者,给DOM添加点击事件就相当于订阅了DOM,当DOM被点击,DOM就会通知js触发‘ alert(“Hello World”) ’
4. 优缺点
优点
1)观察者和被观察者是抽象耦合的。
2)建立一套触发机制
缺点
1)如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2)如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3)观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
三、装饰者模式
1. 定义
建立拥有共同超类的装饰者与被装饰者来实现功能的动态扩展
2. 核心
-
添加功能时不改变原对象结构。
-
装饰对象和原对象提供的接口相同,方便按照源对象的接口来使用装饰对象。
-
装饰对象中包含原对象的引用。即装饰对象是真正的原对象包装后的对象。
3. 举例
// 建立一架飞机
var Plane = function(){}
// 飞机有发射普通子弹的方法
Plane.prototype.fire = function(){
console.log( '发射普通子弹' );
}
// 增加两个装饰类,分别是导弹和原子弹
var MissileDecorator = function( plane ){
this.plane = plane;
}
MissileDecorator.prototype.fire = function(){
this.plane.fire();
console.log( '发射导弹' );
}
var AtomDecorator = function( plane ){
this.plane = plane;
}
AtomDecorator.prototype.fire = function(){
this.plane.fire();
console.log( '发射原子弹' );
}
// 看看测试结果
var plane = new Plane();
plane = new MissileDecorator( plane );
plane = new AtomDecorator( plane );
plane.fire();
// 分别输出: 发射普通子弹、发射导弹、发射原子弹
这种给对象动态增加职责的方式,并没有真正地改动对象自身,而是将对象放入另一个对象之中,这些对象以一条链的方式进行引用,形成一个聚合对象。这些对象都拥有相同的接口(fire方法),当请求达到链中的某个对象时,这个对象会执行自身的操作,随后把请求转发给链中的下一个对象。
XObject o =new XDecorator( new XXDecorator( new XXXDecorator( new XXXXDecorator())));
4. 优缺点
优点
装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点
多层装饰比较复杂。
四、工厂模式
1. 定义
封装对象的创建过程,包括工厂方法模式和抽象工厂模式
工厂方法模式 是工厂模式的核心
定义一个抽象的“工厂方法”来负责new对象,由该类的扩展类来实现如何new的过程
这种方式成功分离了对象创建过程与对象行为,确切地说是把对象创建“下放”到了子类。
抽象工厂模式 是对工厂方法模