设计模式重点在于设计,好的设计能够解决代码编程过程中的痛点。
- 首先提出一个问题:为什么要使用装饰器模式?装饰器模式解决了什么问题?
装饰器模式能够解决类继承的子类多,难以维护问题。当需求变更时,就需要对大量的子类需要进行修改。装饰器模式能够使代码修改遵循开放-封闭原则——对扩展开放,对修改封闭。
-
装饰器模式和类继承
相同点,都是为了给类扩展功能。区别如下: 装饰器模式:动态的增加功能(对扩展开放)。在不改变对象自身的基础上(对修改封闭),装饰器是在运行时给对象添加职责。类似穿衣,天热和天冷可以添加不同的衣服。 类继承:子类继承父类,无论子类是否需要该功能,都要全盘接收。继承在编译阶段就确定好的,灵活性不如装饰器模式。
JavaScript中实现,传统的面向对象的装饰器模式通过对象委托的方式实现,如下: (注:在Java中,装饰器的类型要和被修饰类继承同一个抽象类(抽象类或接口),原因是装饰器的类可以完全替代被修饰类)
// 代码截选自《**JavaScript设计模式**》
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('发射导弹');
}
实际代码使用时,直接使用MissileDecorator对象即可。JavaScript语言非常灵活,其实也可以像Java一样,Plane和MissileDecorator都可以继承自同一个基对象,装饰器对象可以无缝替换被装饰对象,例如:
class Base{
fire(){
throw new Error('子类需要实现该方法')
}
}
装饰器对象和被装饰器对象都继承自该对象,调用fire时,都必须实现该方法,装饰器对象可以完全替换被装饰对象。
基于javascript语言本身特点实现:
var plane = {
fire: function(){
console.log( '发射普通子弹' );
}
}
var missileDecorator = {
fire: function(){
plane.fire()
console.log( '发射导弹' );
}
}
《JavaScript设计模式》一书中使用AOP装饰函数非常经典:
Function.prototype.before = function(beforefn){
_self = this // 指向具体的函数
return function(){
beforefn.apply(this,arguments)
return _self.apply(this,arguments)
}
}
function sayBefore(){
console.log('before')
}
function test(){
console.log('test :>> ', test);
}
test.before(sayBefore)()
基本原理利用了闭包和对象原型
-
装饰器模式 VS 代理模式
装饰器模式和代理模式,同样都对原始对象的功能做了添加,但区别在于:
- 设计目标不同:装饰器模式是为了给对象动态添加行为,解决继承子类众多的问题;代理模式是为了给对象的访问添加一个代理对象
- 可扩展性不同:装饰器模式可以形成一条装饰器链;代理模式只有一层,代理原始对象。