设计模式 - 装饰器模式

466 阅读2分钟

装饰器模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。其并不深入依赖于对象是如何创建的,而是专注于扩展它的功能上。

优点

  1. 装饰类和被装饰类独立,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

缺点

  1. 多层装饰比较复杂。

在实现具体代码前我们需要你对 ES6 装饰器 有个基础的了解

下面我我们实现一个基于 axios 和 装饰器的接口构造方法 先看下最终的如何去使用

import { GET, POST } from "./axiosHelp.js";
// 首页接口 HomeAjax.js
class HomeAjax {
  @GET("/api/user")
  getUserInfo() {}

  @POST("/api/userName")
  setUserName(params) {}
}

export default new HomeAjax();

// home.js
import HomeAjax from "./HomeAjax";
HomeAjax.getUserInfo();
HomeAjax.setUserName({ userName: "装饰器模式" }).then((res) => {
  console.log("修改用户名成功");
});

可以看到上面定义接口时使用了 GET POST 等修饰函数去修饰对应的接口函数,直观上感觉会比较清晰和统一,那么具体如何去实现呢?

// axiosHelp.js
import axios from "axios";

// 1. 构建一个 axios 实例
const axiosInstance = axios.create({ baseURL: "/" });

// 2. 实现基本的 Request 方法去封装 GET/POST
const Request = (method) => (url) => (
  target,
  name,
  descriptor,
) => {
  descriptor.value = function (params) {
    const axiosConfig = {
      method,
      url: url,
    };
    // 构造 axios 需要的参数
    if (params) {
      if (method === "get") {
        axiosConfig.params = params;
      } else {
        axiosConfig.data = params;
      }
    }

    const req = axiosInstance({ ...axiosConfig });

    return req;
  };
  return descriptor;
};

// 3. 用封装好的 Request 方法分别构造 GET/POST 装饰函数
export const GET = Request("get");
export const POST = Request("post");

以上就是基于 ES6 实现的装饰器模式,实际生产中的 Request 会很复杂其涉及到不同的请求体如 form 类型 json 类型,url 类型,路径参数, encode,异常处理,全局 loading 等。这里做了简化是为了方便大家直观的感受装饰器模式的实现。

我对装饰器模式的理解就是装饰函数使用起来简单且清晰,一看就是到这个方法被装饰后的作用。但是缺点也很明显就是装饰函数的实现比较晦涩,而且多重装饰会比较复杂。

注意:目前浏览器尚未支持该特性,需要 babel 转 stage2 的标准, typescript 需要开放 experimentalDecorators 配置。当然也可以使用传统 function 模式的装饰器,即类似高阶函数的方式。