设计模式重构代码_TypeScript 设计模式与重构技巧 · 装饰者模式

如果想要增强对象的功能,不妨给它加个装饰吧~

基本介绍

TypeScript 中已有内置的装饰者模式实现,但是这里还是需要介绍下装饰者模式详细的类结构设计。

先来看看装饰者模式的经典实现(也可以直接跳到下面的案例分析部分)

8b7d052792b8fac93ad59dac546af332.png

不过这里的 UML 只是简略的告诉我们类的设计范式,详细内容我们还是看下代码:

我们期望的使用的方式大概是这样:

const comp = new ConcreteComponent();
const decoratorA = new ConcreteDecoratorA();
const decoratorB = new ConcreteDecoratorB();

decoratorA.decorate(comp);  // 使用 decoratorA 装饰一层
decoratorB.decorate(decoratorA);  // 使用 decoratorB 再装饰一层

decoratorB.operation();
// default operation
// 装饰类 A 的具体行为
// 装饰类 B 的具体行为
abstract class Component {
  abstract operation();
}

// 具体要被装饰的类
class ConcreteComponent extends Component {
  operation() {
    console.log(`default operation`);
  }
}

// 抽象装饰类
abstract class Decorator extends Component {
  protected comp: ComponentNode;
  decorate(comp: ComponentNode) {
    this.comp = comp;
  }
  operation() {
    this.comp.operation();
  }

  decorateBehavior() {}
}

// 具体的装饰者
class ConcreteDecoratorA extends Decorator {
  operation() {
    super.operation();
    this.addBehavior();
  }

  addBehavior() {
    console.log(`装饰类 A 的具体行为`);
  }
}

class ConcreteDecoratorB extends Decorator {
  operation() {
    super.operation();
    this.addBehavior();
  }

  addBehavior() {
    console.log(`装饰类 B 的具体行为`);
  }
}

案例分析

假设在某些接口中,我们希望可以监测返回值是否包含 message 字段,如果是不包含则打印异常

function getResult(): HttpResult {
  return this.http.get(`url`);
}

初版代码:

// 验证是否包含 message 字段
function isValid(value): boolean {
  if(value && value['message'] ) return true;
  else return false;
}

function getResult(): HttpResult {
  const ret = this.http.get(`url`);
  if(!isValid(ret)) console.warn('result 未包含 message 字段!');
  return ret;
}

这样的代码有一个很严重的问题就是侵入性太强,其实判断是否有 message 字段与发送请求是松耦合的,我们不应该将 isValid 函数直接写入到 getResult 函数体中,那再来看看改进版的代码吧~

改进版:

// 验证是否包含 message 字段
function isValid(value): boolean {
  if(value && value['message'] ) return true;
  else return false;
}

function oldGetResult(): HttpResult {
  return this.http.get(`url`);
}

function isValidDecorator(ret: HttpResult): HttpResult {
  if(!isValid(ret)) console.warn('result 未包含 message 字段!');
  return ret;
}

function getResult(): HttpResult {
  return isValidDecorator(oldGetResult());
}

这样看起来要比之前好很多,不过 TypeScript 为我们提供了语言内置的装饰器功能,我们来看看使用 TS 的装饰器写起来会是怎样。

装饰器版:

不过我们需要先将这个函数包裹在类中作为一个方法来使用

class ResultService {
  getResult(): HttpResult {
    return this.http.get(`url`);
  }
}

再编写我们的装饰器:

function isValid(value): boolean {
  return value && value['message'] ? true : false;
}

export function isValidDecorator(
  target: Object,
  propertyName: string,
  propertyDescriptor: PropertyDescriptor
): PropertyDescriptor {
  const method = propertyDescriptor.value;
  propertyDescriptor.value = function(...args: any[]) {
    const result = method.apply(this, args);
    // 原有的验证方法
    if (!isValid(result)) {
      console.warn('result 未包含 message 字段!');
    }
    // 返回调用函数的结果
    return result;
  };
  return propertyDescriptor;
}

如果想要对 TypeScript 的装饰器做更深的了解,可以看看这里的中文文档

使用时非常的方便

class HttpReq {
  // 只需添加这一行代码
  @isValidDecorator
  getResult() {
    return this.http.get(`url`);
  }
}

总结:

  • 想要优雅的增强对象的功能,装饰者模式是个不错的选择
  • TypeScript 中也已内置装饰器

欢迎阅读 TypeScript 设计模式与重构技巧 系列的其它文章:

Wave9:TypeScript 设计模式与重构技巧 · 开篇​zhuanlan.zhihu.com
8cae49a01d526a1d5d0a8ba287d7009f.png
Wave9:TypeScript 设计模式与重构技巧 · 工厂模式​zhuanlan.zhihu.com
47befe72fb55a6c97462aa903d2c0f38.png
Wave9:TypeScript 设计模式与重构技巧 · 建造者模式​zhuanlan.zhihu.com
47b46a7f5e4b8a49650b23375bcd403d.png
Wave9:TypeScript 设计模式与重构技巧 · 单例模式​zhuanlan.zhihu.com
7c84ff0e247944b0e8f785053679f307.png
Wave9:TypeScript 设计模式与重构技巧 · 桥接模式​zhuanlan.zhihu.com
9ac2b931be85c34155a253906db2f653.png
Wave9:TypeScript 设计模式与重构技巧 · 组合模式​zhuanlan.zhihu.com
8bf0e71a2b4def9bac97235f691b43a7.png
Wave9:TypeScript 设计模式与重构技巧 · 适配器模式​zhuanlan.zhihu.com
975a81f461361a3c3d66c76e2d3bd08d.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值