likes
comments
collection
share

前端设计模式之装饰器模式(八)

作者站长头像
站长
· 阅读数 45
  • 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构
  • 这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装
  • 动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活
  • 例如,手机上套一个壳可以保护手机,壳上粘一个指环,这样手机的功能一点都没变,只是为其装饰附加了一些其他的功能
function decorate(phone) {
  phone.fn2 = function () {
    console.log("指环");
  };
}
const phone = {
  name: "iphone12",
  fn1() {},
};
const newPhone = decorate(phone);

实现装饰器模式

前端设计模式之装饰器模式(八)

class Circle {
  draw() {
    console.log("画一个圆");
  }
}

class Decorator {
  private circle: Circle;
  constructor(circle: Circle) {
    this.circle = circle;
  }
  draw() {
    this.circle.draw();
    this.setBorder();
  }
  private setBorder() {
    console.log("设置边框颜色");
  }
}

const circle = new Circle();
circle.draw();

// 装饰draw方法,增加setBorder功能
const decorator = new Decorator(circle);
decorator.draw();

是否符合设计原则?

  • 装饰器和目标分离,解耦
  • 装饰器可自行扩展
  • 目标也可自行扩展

符合开放封闭原则

场景

  • ES 引入了 Decorator 语法,TS 也支持
  • 装饰器其实就是一个函数

PS:在 tsconfig.json 中加 experimentalDecorators: true

装饰 class

// 装饰器
function testable(target: any) {
  target.isTestable = true;
}

@testable
class Foo {
  static isTestable?: boolean;
}

console.log(Foo.isTestable); // true

可以传入参数:

// 装饰器工厂函数
function testable(val: boolean) {
  // 装饰器
  return function (target: any) {
    target.isTestable = val;
  };
}

@testable(false)
class Foo {
  static isTestable?: boolean;
}

console.log(Foo.isTestable); // false

装饰 class 方法

function readOnly(target: any, key: string, descriptor: PropertyDescriptor) {
  // console.log("target", target) // Foo实例
  // console.log("key", key)  // getName
  descriptor.writable = false;
}

function configurable(val: boolean) {
  return function (target: any, key: string, descriptor: PropertyDescriptor) {
    descriptor.configurable = val;
  };
}

class Foo {
  private _name = "张三";
  private _age = 20;

  @readOnly
  getName() {
    return this._name;
  }

  @configurable(false)
  getAge() {
    return this._age;
  }
}

const f = new Foo();
// f.getName = () => { return "hello" } // 会报错
console.log(f.getName());

// @ts-ignore
// console.log( Object.getOwnPropertyDescriptor(f.__proto__, "getAge") ) // { configurable: false }

PS:其实 TS 本身有 readOnly 语法

AOP

  • AOP - Aspect Oriented Programming 面向切面编程
  • 业务和系统基础功能分离,用 Decorator 很合适

前端设计模式之装饰器模式(八)

实现 log

function log(target: any, key: string, descriptor: PropertyDescriptor) {
  const oldValue = descriptor.value; // fn1 函数

  // 重新定义 fn1 函数
  descriptor.value = function () {
    console.log("记录日志...");
    return oldValue.apply(this, arguments);
  };
}

class Foo {
  @log // 不影响业务功能的代码,只是加了一个 log 的“切面”
  fn1() {
    console.log("业务功能1");
  }
}

const f = new Foo();
f.fn1(); // 记录日志...  业务功能1
转载自:https://juejin.cn/post/7197759313577508919
评论
请登录