关于前端中常用的js设计模式

711 阅读3分钟

策略模式

定义 : 要实现某一个功能,有多种方案可以选择。我们定义策略,把它们一个个封装起来,并且使它们可以相互转换。

// 维护权限列表
const jobList = ['FE', 'BE'];

// 策略
var strategies = {
  checkRole: function(value) {
    return value === 'juejin';
  },
  checkGrade: function(value) {
    return value >= 1;
  },
  checkJob: function(value) {
    return jobList.indexOf(value) > 1;
  },
  checkEatType: function(value) {
    return value === 'eat melons';
  }
};

我们已经写完了策略,接下来要做的就是验证了~

// 校验规则
var Validator = function() {
  this.cache = [];

  // 添加策略事件
  this.add = function(value, method) {
    this.cache.push(function() {
      return strategies[method](value);
    });
  };

  // 检查
  this.check = function() {
    for (let i = 0; i < this.cache.length; i++) {
      let valiFn = this.cache[i];
      var data = valiFn(); // 开始检查
      if (!data) {
        return false;
      }
    }
    return true;
  };
};

此时,某同学需要进行权限验证的条件为 :

  • 掘金用户
  • 掘金等级 1 级以上

那么代码就可以这么写 :

// 小彭使用策略模式进行操作
var compose1 = function() {
  var validator = new Validator();
  const data1 = {
    role: 'juejin',
    grade: 3
  };
  validator.add(data1.role, 'checkRole');
  validator.add(data1.grade, 'checkGrade');
  const result = validator.check();
  return result;
};

什么时候用策略模式?

当你负责的模块,基本满足以下情况时

  • 各判断条件下的策略相互独立且可复用
  • 策略内部逻辑相对复杂
  • 策略需要灵活组合

发布-订阅模式

发布-订阅是一种消息范式,消息的发布者,不会将消息直接发送给特定的订阅者,而是通过消息通道广播出去,然后订阅者通过订阅获取到想要的消息。

const EventEmit = function() {
  this.events = {};
  this.on = function(name, cb) {
    if (this.events[name]) {
      this.events[name].push(cb);
    } else {
      this.events[name] = [cb];
    }
  };
  this.trigger = function(name, ...arg) {
    if (this.events[name]) {
      this.events[name].forEach(eventListener => {
        eventListener(...arg);
      });
    }
  };
};

上边我们写好了一个 EventEmit,然后我们的业务代码可以改成这样 ~

let event = new EventEmit();
event.trigger('success');

MessageCenter.fetch() {
  event.on('success', () => {
    console.log('更新消息中心');
  });
}
Order.update() {
  event.on('success', () => {
    console.log('更新订单信息');
  });
}
Checker.alert() {
  event.on('success', () => {
    console.log('通知管理员');
  });
}

什么时候用发布-订阅模式?

当你负责的模块,基本满足以下情况时

  • 各模块相互独立
  • 存在一对多的依赖关系
  • 依赖模块不稳定、依赖关系不稳定
  • 各模块由不同的人员、团队开发

适配器模式

为了解决不兼容的问题,把一个类的接口换成我们想要的接口。

三个数据来源,三种时候数据结构,我们的资源列表组件是只能接收一种数据格式的列表时可以使用适配器模式

首先,定义一个统一的数据格式:AdapterResourceFileType

export interface AdapterResourceType {
  uuid: string;
  created: number;
  fileNo: number;
  fileName: string;
  fileOrigin: string;
  fileStatus: string;
  fileInfo: {
    type: number;
    size?: number;
    [key: string]: any;
  };

代理模式

说到代理模式,可以联想到“事件代理”。

代理模式是为其它对象提供一种代理以控制这个对象的访问,具体执行的功能还是这个对象本身,就比如说,我们发邮件,通过代理模式,那么代理者可以控制,决定发还是不发,但具体发的执行功能,是外部对象所决定,而不是代理者决定。

举一个来自js设计模式这本书的例子。

// 本体
var domImage = (function() {
  var imgEle = document.createElement('img');
  document.body.appendChild(imgEle);
  return {
    setSrc: function(src) {
      imgEle.src = src;
    }
  };
})();

// 代理
var proxyImage = (function() {
  var img = new Image();
  img.onload = function() {
    domImage.setSrc(this.src); // 图片加载完设置真实图片src
  };
  return {
    setSrc: function(src) {
      domImage.setSrc('./loading.gif'); // 预先设置图片src为loading图
      img.src = src;
    }
  };
})();

// 外部调用
proxyImage.setSrc('./product.png');

什么时候用代理模式?

当你负责的模块,基本满足以下情况时

  • 模块职责单一且可复用
  • 两个模块间的交互需要一定限制关系

责任链模式

什么是责任链模式呢? 避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

const Chain = function(fn) {
  this.fn = fn;
  
  this.setNext = function() {}

  this.run = function() {}
}

const applyDevice = function() {}
const chainApplyDevice = new Chain(applyDevice);

const selectAddress = function() {}
const chainSelectAddress = new Chain(selectAddress);

const selectChecker = function() {}
const chainSelectChecker = new Chain(selectChecker);

// 运用责任链模式实现上边功能
chainApplyDevice.setNext(chainSelectAddress).setNext(chainSelectChecker);
chainApplyDevice.run();

什么时候使用责任链模式?

当你负责的模块,基本满足以下情况时

  • 你负责的是一个完整流程,或你只负责流程中的某个环节
  • 各环节可复用
  • 各环节有一定的执行顺序
  • 各环节可重组

参考 唠嗑一下前端中的设计模式(真实场景例子)