设计模式--装饰器模式
概念:
装饰模式(Decorator Pattern) :动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。
其实,就是创建一个包装器,里面有增强的功能,然后把目标对象给套住,就直接使用包装器对象。
UML:
es6:
class Circle { draw() { console.log("画一个圆形"); } } class Decorator { constructor(circle){ this.circle = circle; } draw() { this.circle.draw(); this.setRedBorder(this.circle); } setRedBorder(circle) { console.log("设置红色边框") } } // 测试代码 let circle = new Circle(); let decorator= new Decorator(circle); decorator.draw();
es7:
function borderDecorator(target, key, descriptor) { const method = descriptor.value; descriptor.value = () => { method.apply(target); console.log("设置红色边框") } return descriptor; } class Circle { @borderDecorator draw() { console.log("画一个圆形"); } } // 测试代码 let circle = new Circle(); circle.draw();
对某个函数增强功能:
const memoize = function(f) { const cache = {}; return function() { const argStr = JSON.stringify(arguments); if (!cache[argStr]) { cache[argStr] = f.apply(f, arguments); } return cache[argStr]; } }
函数f没有缓存功能, 在没有修改原函数的情况下,通过闭包,套了一层,就具有缓存的功能了。
优点:
- 装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
- 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。
- 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
- 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”
缺点:
- 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。
- 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。