装饰者模式是一种结构型设计模式,目的是为了促进代码复用。装饰者模式与Mixin很相似,可以算是一种子类化的替代方案。
装饰者模式通常是为了添加系统的现有能力,为系统中的对象添加额外的功能,同时不影响到系统底层代码(这些额外能力父类可能并不需要,只有部分子类需要)

简单实现

  1. // 场景描述:当你购买macbook时,可以通过cost方法来获得最终的价格,
  2. // 我们通过装饰者来扩展cost方法,当增加不同配置时,扩展cost计算方法
  3. // macbook类
  4. function MacBook (screenSize) {
  5. this.cost = function (){
  6. if (screenSize == 13) {
  7. return 999
  8. } else {
  9. return 1199
  10. }
  11. }
  12. }
  13. // 装饰者1:增加内存配置,费用上升100
  14. function Memory (macbook) {
  15. var v = macbook.cost();
  16. macbook.cost = function () {
  17. return v + 100
  18. }
  19. }
  20. // 装饰者2:增加硬盘容量,费用上升250
  21. function HardDiscCapacity (macbook) {
  22. var v = macbook.cost();
  23. macbook.cost = function () {
  24. return v + 250
  25. }
  26. }
  27. // 装饰者3:延长质保期,费用上升120
  28. function Insurance (macbook) {
  29. var v = macbook.cost();
  30. macbook.cost = function () {
  31. return v + 120
  32. }
  33. }
  34. // 使用装饰者
  35. var mb = new MacBook(13)
  36. Memory(mb)
  37. HardDiscCapacity(mb)
  38. Insurance(mb)
  39. console.log(mb.cost())
  40. // 输出1469
  41. //装饰者只影响需要装饰的实例
  42. var mb2 = new MacBook(13)
  43. console.log(mb2.cost())
  44. // 仍然输出999

在上面的例子中,装饰者重写了MacBook类的cost方法。相比直接修改MacBook的cost方法,这样的写法使我们的代码更易读,也更佳健壮。

优点

对象可以被新的行为包装或装饰,从而继续使用,并且不用担心被使用的基本对象被修改,在一些场景下,我们可以减少需要大量子类才能实现的开发复杂度。

缺点

过度放开任意扩展,会导致对象复杂变得难以管理,不熟悉这个模式的开发人员也会难以理解为何如此使用。

ES7 Decorator

es7里的Decorator(@),借鉴于python,可用于对类、方法进行装饰(不可对函数进行装饰)。通过使用Decorator可以达到装饰者模式、Mixin模式等的效果,如下:

  1. // Mixin.js
  2. function mixins(...list) {
  3. return function (target) {
  4. Object.assign(target.prototype, ...list);
  5. };
  6. }
  7. // 使用mixin
  8. // 需要混入的方法
  9. const Foo = {
  10. foo() { console.log('foo') }
  11. };
  12. // 通过decorator混入
  13. @mixins(Foo)
  14. class MyClass {}
  15. let obj = new MyClass();
  16. obj.foo() // "foo"
  1. // 使用decorator实现一个事件触发记录器,同时能够重用
  2. function logger(topic) {
  3. return function(target, name, descriptor) {
  4. const fn = descriptor.value;
  5. descriptor.value = function() {
  6. console.log('before' + topic)
  7. let value = fn.apply(this, arguments);
  8. console.log(value)
  9. console.log('after' + topic)
  10. };
  11. };
  12. }
  13. class FooComponent {
  14. @logger('HoldMessage')
  15. holdMessage() {
  16. return { my: 'data' };
  17. }
  18. @logger('KeepMessage')
  19. keepMessage() {
  20. return { my: 'data2' };
  21. }
  22. }
  23. let foo = new FooComponent();
  24. foo.holdMessage();
  25. // beforeHoldMessage
  26. // { my: 'data' }
  27. // afterHoldMessage

具体Decorator语法和使用场景,不在本篇中进行详细描述,请大家参考ES6相关的文章