设计模式系列:策略模式

案例引入

昨天在调试同事开发的页面,发现有很多 404 的请求,看了下 Network 面板有点哭笑不得,原来是前端把页面路由当成后台接口请求了,后台肯定不存在对应的资源,因为是前端路由。那么为什么会发送这样的请求呢?

我看了下同事写的代码,由于这边的需求是需要同时支持 mock 环境和正常后台环境,然后根据传递的 query 字段选择合适的接口进行请求。然鹅看到代码就惊呆了,各种 IF ELSE 和 SWITCH CASE ,夸张的是同事告诉我这是他能想到的最优方案(手工狗头)

const isMockEnv = process.env.USING_MOCK_ENV === 'true';

function fetchData(query) {
  let reqUrl = "";
  if (isMockEnv) {
    switch (query) {
      case "api_1":
        reqUrl = "/api/api_1";
        break;
      case "api_2":
        reqUrl = "/api/api_2";
        break;
      case "api_3":
        reqUrl = "/api/api_3";
      default:
        break;
    }
  } else {
    switch (query) {
      case "api_1":
        reqUrl = "/mock/api/api_1";
        break;
      case "api_2":
        reqUrl = "/mock/api/api_2";
        break;
      case "api_3":
        reqUrl = "/mock/api/api_3";
      default:
        break;
    }
  }
  return request({
    url: reqUrl,
    method: "get"
  })
}

上面只是一个简单示意,实际接口还要多,而且不同的接口需要带不同的 query 参数,不能简单地通过字符串拼接实现

然后通过 debug 可以发现,有时候给 fetchData 传递的参数 SWITCH CASE 里面没有匹配上,导致 reqUrl 最后拿到的是空字符串,因此最后 axios 就默认按照当前页面的路由去请求了。

如果仅仅只是修复 bug ,完全没必要为这个写篇文章。这边主要是本人非常看不惯同事写的逻辑,于是决定用策略模式优化一下:

const switchQueryStrategy = () => {
  const isMockEnv = process.env.USING_MOCK_ENV === 'true';
  const urls = {
    "api_1": "/api/api_1",
    "api_2": "/api/api_2",
    "api_3": "/api/api_3"
  }
  const mockUrls = {
    "api_1": "/mock/api/api_1",
    "api_2": "/mock/api/api_2",
    "api_3": "/mock/api/api_3"
  }
  return isMockEnv ? mockUrls : urls;
}

const fetchData = (query) => {
  const queryStrategy = switchQueryStrategy();
  // 处理参数匹配不上的情形
  if (!Object.keys(queryStrategy).includes(query)) {
    return Promise.reject("API Not Found!");
  }
  return request({
    url: queryStrategy[query],
    method: 'get'
  })
}

优化之后感觉好多了,然后同事看了代码却说跟我也差不多嘛,这也叫优化?

差不多?明明差很多好吗,这叫策略模式,不是拍脑袋想出来的,而是有统一规范的。

之所以我们平时开发的时候不需要过多关注设计模式,那是因为框架帮我们把逻辑封装好了,我们只需要按照一个统一的编程风格去开发就行。例如 Vue 的响应式机制是基于观察者模式,事件机制则是通过发布订阅模式等等。但是这不等于不需要了解设计模式了,假如你不了解设计模式,且不说设计框架,就是一个 API 的封装都不一定做得好。

策略模式介绍

策略模式指的是定义了一系例算法,把它们每个都封装起来。将不变的部分和变化的部分隔离开来是设计模式中的一个重要思想,策略模式则是将算法和使用算法两部分的实现拆开,降低耦合度。

基于策略模式的程序至少有两部分组成:策略类和环境类(Context)。

策略类封装了具体的算法,并负责具体的计算过程,可以理解为“执行者”。

环境类(Context)接受客户的请求,然后将请求委托给一个策略类,可以理解为“调度者”。

优点:

  • 隔离算法的实现与使用
  • 运行时可切换算法
  • 用组合代替继承
  • 易扩展,符合开闭原则,无需修改上下文即可引入新策略

缺点:

  • 需要暴露所有的策略接口,用于区分策略差异
  • 如果逻辑条件简单,使用策略模式会增加代码冗余度

表驱动法

策略模式一大特点就是节省判断逻辑。有一个与策略模式类似的概念,叫“表驱动法”,或者叫“查表法”。

表驱动方法是一种使你可以在表中查找信息,而不必用很多的逻辑语句ifCase)来把它们找出来的方法。事实上,任何信息都可以通过表来挑选。在简单的情况下,逻辑语句往往更简单而且更直接。但随着逻辑链的复杂,表就变得越来越富有吸引力了。

举个简单的例子,假设我们要获取当前是星期几,代码可能是这样的:

function getDay() {
  const day = (new Date()).getDay();
  switch (day) {
    case 0:
      return '星期日';
    case 1:
      return '星期一';
    // ...
    case 6:
      return '星期六';
    default:
      return '';
  }
}

如果是初次编程的同学还可能会有 if else 条件语句来判断返回值,代码就会显得比较冗余。

借助表驱动法的思想,这里我们是可以有优化空间的,表驱动法的写法如下:

const days = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
function getDay2() {
  return days[(new Date()).getDay()];
}

将数组的元素和下标对应起来是非常常用的技巧

当然上述只是一个非常简单的🌰,大家在编码过程中只需要主要点,如有涉及类似场景,请用这种方式去编码,体验更愉悦!

参考

新生代农民工也要懂的策略设计模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值