likes
comments
collection
share

JavaScript设计模式:装饰器模式

作者站长头像
站长
· 阅读数 35

JavaScript设计模式:装饰器模式

模式概念

如何可以在不改变一个对象本身功能的基础上给对象增加额外的新功能,这是一种用于替代继承的技术。

装饰器模式(Decorator Pattern)是一种对象结构型设计模式,允许用户向一个对象增加一些额外的职责,同时不改变其结构。这种模式通过创建一个包装对象,来包裹实际对象,从而在不修改实际对象代码的情况下,动态地给对象添加额外的职责。

通过一种不使用继承的情况下动态地给对象添加新的职责,使用对象之间的关联关系取代类之间的继承关系引入了装饰类,在装饰类中既可以调用待装饰的原有类的方法,还可以增加新的方法,以扩展原有类的功能。

模式结构

JavaScript设计模式:装饰器模式

  • Component(抽象组件) :定义了一个对象接口,可以给这些对象动态地添加职责。
  • ConcreteComponent(具体组件) :定义了抽象组件的具体实现,也就是被装饰的具体对象。
  • Decorator(抽象装饰者) :持有一个Component对象的引用,并定义了与Component接口一致的接口。
  • ConcreteDecorator(具体装饰者) :实现Decorator的接口,并添加装饰的功能。

代码实现

// 抽象组件
class Component {
  operate() {
    throw new Error('This method must be implemented by subclasses');
  }
}

// 具体组件
class ConcreteComponent extends Component {
  operate() {
    return 'ConcreteComponent';
  }
}

// 抽象装饰者
class Decorator extends Component {
  constructor(component) {
    super();
    this._component = component;
  }

  operate() {
    return this._component.operate();
  }
}

// 具体装饰者A
class ConcreteDecoratorA extends Decorator {
  operate() {
    const result = this._component.operate();
    return `${result} + ConcreteDecoratorA`;
  }
}

// 具体装饰者B
class ConcreteDecoratorB extends Decorator {
  operate() {
    const result = this._component.operate();
    return `${result} + ConcreteDecoratorB`;
  }
}

// 使用示例
const component = new ConcreteComponent();
//不使用装饰器
console.log(component.operate()); // 输出: ConcreteComponent

//添加一个装饰器
const decoratedComponentA = new ConcreteDecoratorA(component);
console.log(decoratedComponentA.operate()); // 输出: ConcreteComponent + ConcreteDecoratorA

//添加多个装饰器
const decoratedComponentAB = new ConcreteDecoratorB(decoratedComponentA);
console.log(decoratedComponentAB.operate()); // 输出: ConcreteComponent + ConcreteDecoratorA + ConcreteDecoratorB

使用函数的方式:为函数添加前置和后置方法

//为函数添加前置函数
Function.prototype.before = function (beforeFn) {
  const _this = this;
  return function () {
    //先执行前置函数
    beforeFn.apply(this, arguments);
    //执行函数
    return _this.apply(this, arguments);
  };
};
//为函数添加后置函数
Function.prototype.after = function (afterFn) {
  const _this = this;
  return function () {
    //执行函数
    const result = _this.apply(this, arguments);
    //执行后置函数
    afterFn.apply(this, arguments);
    return result;
  };
};

//创建一个函数
function test() {
  console.log("111111");
}

//在原来的函数基础上增加新的功能
const testAll = test
  .after(function () {
    console.log("后", arguments);
    console.log("222222");
  })
  .before(function () {
    console.log("前", arguments);
    console.log("000000");
  });
//再执行包含前置和后置的函数
testAll(11, 22);
/**
 * 打印输出:
 * 前 Arguments(2) [11, 22, callee: ƒ, Symbol(Symbol.iterator): ƒ]
 * 000000
 *
 * 111111
 *
 * 后 Arguments(2) [11, 22, callee: ƒ, Symbol(Symbol.iterator): ƒ]
 * 222222
 */

模式效果

模式优点

  • 动态扩展:可以在不修改原有对象代码的情况下,动态地给对象添加新的职责。
  • 透明性:装饰者和实际对象可以统一看待,对客户端透明。
  • 灵活性:可以叠加多个装饰者,每个装饰者都增加一些新的功能,提供更灵活的扩展方式。

模式缺点

  • 复杂性:使用多个装饰者可能会使系统变得复杂。
  • 难以管理:装饰者数量增多,难以追踪哪些类是装饰者。

模式应用

axios拦截器

Axios 中,拦截器(Interceptors)是一种非常有用的功能,它允许你在请求或响应被 then 或 catch 处理之前,进行一些额外的处理,比如修改请求头、验证响应等。

拦截器模式本质上是一种装饰器模式的应用,提供了一种强大的方式来扩展和修改 HTTP 请求和响应的处理流程。以下是如何在 Axios 中使用拦截器以及它如何体现装饰器模式的:

  • 请求拦截器:在发送请求之前,你可以使用请求拦截器来修改请求的配置,如添加 token、修改请求头等。
  • 响应拦截器:在响应被返回给 then 方法之前,你可以使用响应拦截器来处理响应数据,如错误统一处理、数据格式化等。
// 创建 Axios 实例
const axios = axios.create({
  baseURL: 'http://aaa.com',
});

// 添加请求拦截器
axios.interceptors.request.use(
  config => {
    // 在发送请求之前做些什么
    config.headers.Authorization = `Bearer ${token}`;
    return config;
  },
  error => {
    // 对请求错误做些什么
    return Promise.reject(error);
  }
);

// 添加响应拦截器
axios.interceptors.response.use(
  response => {
    // 对响应数据格式化
    return response;
  },
  error => {
    // 对响应错误做点什么
    // 常见响应码的处理等
    return Promise.reject(error);
  }
);

// 使用 Axios 发送请求
axios.get('/user')
  .then(response => {
    console.log(response.data);
  })
  .catch(error => {
    console.error('There was an error!', error);
  });

React高阶组件

高阶组件(Higher-Order Component, HOC)是一种基于React组合特性的高级技术,它本身并不是React的官方API,而是对组件进行包裹以添加额外功能的一种模式。高阶组件与装饰器模式在概念上非常相似,都用于扩展或增强已有组件的功能。

大家应该对高阶函数都不陌生,高阶函数定义:

  • 接受一个或多个函数作为输入
  • 输出一个函数

JavaScript中比较常见的filtermapreduce等都是高阶函数。

高阶组件其实是一个函数,它接受一个组件作为参数,并返回一个新的组件。这个新的组件通常会包裹原有的组件,并添加一些额外的功能。

class Home extends PureComponent {
  constructor(props) {
    super(props)
  }
  render() {
    return <div>{this.props.name}</div>
  }
}

//定义高阶组件
function enhanceComponent(WapperdComponent) {
  class NewComponent extends PureComponent {
    render() {
      return <WapperdComponent {...this.props} />
    }
  }
  ...添加一些额外功能
  NewComponent.displayName = "Kobe" //使用displayName属性修改组件(在devtools中)展示的名字,很少改动
  return NewComponent
}
const EnhanceHome = enhanceComponent(Home)

Typescript装饰器

在 TypeScript 中,装饰器是一种特殊类型的声明,它可以被附加到类、方法、属性或参数上。TypeScript 装饰器提供了一种方式,允许你在不修改原始类或对象的情况下,以声明式的方式给类或对象添加额外的功能,也是装饰器模式的思想。

在 TypeScript 中,装饰器使用 @expression 这种语法,其中 expression 是一个返回值的函数,这个函数会在运行时被调用。

TypeScript 装饰器的种类:

  1. 类装饰器 (@Class):装饰整个类。
  2. 方法装饰器 (@Method):装饰类的某个方法。
  3. 访问器装饰器 (@Getter, @Setter):装饰类的 getter 和 setter。
  4. 属性装饰器 (@Prop):装饰类的属性。
  5. 参数装饰器 (@Param):装饰方法的参数。

方法装饰器

const methodWatcher: MethodDecorator = <T>(
  target: Object,
  propertyKey: string | symbol,
  descriptor: TypedPropertyDescriptor<T>
) => {
  console.log(target); // {}  对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
  console.log(propertyKey); // getName  方法名
  console.log(descriptor); //属性描述符对象
  /**
   * {
   *   value: [Function: getName],  //方法
   *   writable: true,   //是否可写
   *   enumerable: false,   //是否可枚举
   *   configurable: true   //是否可配置
   * }
   */
};

@watcher
@watchers<string>("哈哈哈")
class A {
  @propWatcher<string>("嘿嘿嘿")
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  @methodWatcher
  getName() {
    return this.name;
  }
}

大家有兴趣可以学习一下TypeScript装饰器。

除了TypeScript装饰器,Nest框架的核心也是装饰器模式,Nestjs是一个nodejs服务器框架, 实现了依赖注入、路由处理、参数解析、中间件应用等功能,使得代码更加模块化、可维护,并且易于测试。装饰器模式在Nestjs中的应用,展示了如何通过声明式编程来增强代码的可读性和可扩展性。非常优秀,推荐大家学习。

今天的分享就到这里,希望可以帮助到你!假如你对文章感兴趣,可以来我的公众号:小新学研社。

JavaScript设计模式:装饰器模式

转载自:https://juejin.cn/post/7395623789133971491
评论
请登录