使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
一、职责链模式
1.基本实现
/**
* 下面,以一个发放奖学金的例子为例,学生的评级为A,发放奖学金1500,评级B,发放1000,评级C,发放500;
*/
//基本实现
let order = function (type) {
if (type === 'A') {
return 1500;
} else if(type === 'B') {
return 1000;
}
return 500;
}
//职责链模式实现
let orderA = function (type) {
if (type == 'A') {
return 1500;
}
return orderB(type);
}
let orderB = function (type) {
if (type === 'B') {
return 1000;
}
return orderC(type);
}
let orderC = function (type) {
if (type === 'C') {
return 500;
}
throw new Error('参数输入有误')
}
通过以上实现了一个基本的职责链模式,用户可以从职责链的任意结点开始调用,直到请求被传给可以处理请求的结点。但如果之后新增了奖学金金评级,或者需要在评级结点中插入结点,则需要进入原代码中修改代码,这很违反开闭原则,因此对其进行以下修改。
//灵活可拆分的职责链结点
let orderA = function (type) {
if (type == 'A') {
console.log(1500);
} else {
this.next.passRequest(type);
}
}
let orderB = function (type) {
if (type === 'B') {
console.log(1000);
} else {
this.next.passRequest(type);
}
}
let orderC = function (type) {
if (type === 'C') {
console.log(500);
} else {
throw new Error('参数输入有误');
}
}
let Chain = function (fn) {
this.fn = fn;
this.next = null;
}
Chain.prototype.setNext = function (next) {
this.next = next;
}
Chain.prototype.passRequest= function() {
this.fn.apply(this, arguments);
}
let chainA = new Chain(orderA);
let chainB = new Chain(orderB);
let chainC = new Chain(orderC);
chainA.setNext(chainB);
chainB.setNext(chainC);
chainA.passRequest('B'); //1000
通过以上代码,我们可以灵活的对职责链中的节点进行增删.
2.职责链模式的优缺点
(1)优点
解耦了请求发送者和N个接收者之间的负责关系
链中的节点可以灵活地拆分重组
可以手动指定起始节点
(2)缺点
不能保证某个请求一定会被链中的节点处理
当职责链过长时,会带来一定的性能损耗
二、装饰者模式
在不改变对象自身的基础上,在程序运行期间给对象动态地添加职责。
1.基本的装饰者模式实现
//下面以一个飞机大战游戏为例,飞机一开始默认只能发射普通子弹,二级可以发射导弹,三级可以发射激光炮
let Plane = function () { };
Plane.prototype.fire = function () {
console.log('发射普通子弹');
}
let MissileDecorator = function (plane) {
this.plane = plane;
}
MissileDecorator.prototype.fire = function () {
this.plane.fire();
console.log('发射导弹');
}
let LaserDecorator = function (plane) {
this.plane = plane;
}
LaserDecorator.prototype.fire = function () {
this.plane.fire();
console.log('发射激光炮');
}
let plane = new Plane;
plane = new MissileDecorator(plane);
plane = new LaserDecorator(plane);
plane.fire(); //发射普通子弹 发射导弹 发射激光炮
在上述代码中,分别创建了两个装饰者类,装饰者类有着和被装饰者同样的方法名称,且装饰者在执行自己本身方法的同时,还会去执行被装饰者的方法。通过这样的方式,可以给对象动态地增加职责,且不会更改对象本身。
2.JavaScript中的装饰者
//在上面的例子中,我们模仿传统面向对对象语言的方式,使用类完成了一个装饰者模式的设计,但是在js中其实并不需要仿照类的模式
let plane = {
fire: function () {
console.log('发射普通子弹');
}
}
let missileDecorator = function () {
console.log('发射导弹');
}
let laserDecorator = function () {
console.log('发射激光炮');
}
let fire1 = plane.fire;
plane.fire = function () {
fire1();
missileDecorator();
}
let fire2 = plane.fire;
plane.fire = function () {
fire2();
laserDecorator();
}
plane.fire();//发射普通子弹 发射导弹 发射激光炮
3.装饰函数
在上述JavaScript方式实现装饰者模式的代码中,我们通过保存原来的fire方法,来达到给fire方法添加新行为的目的,这这方式同样也是一种在开发中比较常用的方式。有时候我们在想给window绑定onload事件时,不确定该事件是否已经被绑定过,那么我们可以在新绑定的事件中,保存原先的window.onload,然后在新的onload函数中执行。
(1)缺点
- 必须维护中间变量
- 容易this劫持问题
4.使用AOP装饰函数
//Function.prototype.before方法和Function.prototype.after方法
Function.prototype.before = function (fn) {
let _self = this;
return function () {
fn.apply(this, arguments);
return _self.apply(this, arguments);
}
}
Function.prototype.after = function (fn) {
let _self = this;
return function () {
let res = _self.apply(this, arguments);
fn.apply(this, arguments);
return res;
}
}
let func = function (num) {
console.log(num);
}
func = func.before(function () {
console.log(1);
}).after(function () {
console.log(3)
});
func(2); // 1 2 3