7、设计模式行为型之策略模式

本文介绍了策略模式在处理复杂条件判断时的应用,以差旅报销费用为例,从最初的冗余if...else结构,逐步演变为使用映射对象来封装策略,实现了代码的解耦和可扩展性。通过这种方式,当需要添加新的职级或规则时,无需修改原有函数,提高了代码的可维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

设计模式行为型之策略模式

 
今天刚刚入职新公司,各个流程还没有走完,闲来无事就先来讲一讲策略模式。

策略模式其实是一种应该去“意会”的模式。

我们可以先来看看策略模式的定义:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

是不是看到这个定义会一脸蒙,没有关系,大部分同学看到这个定义的第一感觉都是这样,让我们先放下这个定义,来看看下面的例子吧。


在一家公司中,不同的员工对应的不同的职级,就拿差旅报销费用来举例,从 P1 到 p5 每个职级都是不一样的。

我们假设 p1 是 200 元一天,每升一级可以多加 50 元,那里 p2 就是 250 元,p3 就是 300 元 依次类推。
 

看看下面这个写法怎么样:

/**
 * 职级对应差旅处理,输入职级,输出对应差旅费
 */
function travelExpense(level) {
  if (level === "p1") {
    return 200;
  }
  if (level === "p2") {
    return 200 + 1 * 50;
  }
  if (level === "p3") {
    return 200 + 2 * 50;
  }
  if (level === "p4") {
    return 200 + 3 * 50;
  }
  if (level === "p5") {
    return 200 + 4 * 50;
  }

}

这是一段非常容易理解的代码,五个职级对应五个差旅费。

但是我们此时需要再增加一个需求,要根据地区来做更细的调整,例如你如果去北上广,那么根据职级不同,还需要对差旅费进行调整。就像这样:

/**
 * 职级对应差旅处理,输入职级,输出对应差旅费
 * area 的值分别对应一类,二类,三类地区
 */
function travelExpense(level, area) {
  if (level === "p1") {
    let price = 200
    if (area === 1) {
      price += 200
    } else if (area === 2) {
      price += 100
    } else if (area === 3) {
      price += 50
    }
    return price;
  }
  if (level === "p2") {
    // ... 省略 area 相关判断代码
    return 200 + 1 * 50;
  }
  if (level === "p3") {
    // ... 省略 area 相关判断代码
    return 200 + 2 * 50;
  }
  if (level === "p4") {
    // ... 省略 area 相关判断代码
    return 200 + 3 * 50;
  }
  if (level === "p5") {
    // ... 省略 area 相关判断代码
    return 200 + 4 * 50;
  }

}

我们仅为了展示,因此只在 p1 级别中添加了 area 判断的代码,但是你应该可以想象到,完整添加 area 判断后的代码,真的算得上是又臭又长。

并且试想一下,如果后期还需要增加或者删除其他逻辑,你只能去修改 travelExpense 这个魔鬼函数,同时你的每次修改都会影响整个函数功能,你不得不告诉测试同学需要重新测试全部流程。我想这时候测试同学的心里早就万马奔腾了。

在这种情况下,为了不被其他同事吐槽,也本着负责任的心态。我们需要来优化这段逻辑,

/**
 * 差旅处理函数
 */
function travelExpense(level, area) {
    if (level === "p1") {
      return travelExpenseToP1(area);
    }
    if (level === "p2") {
      return travelExpenseToP2(area);
    }
    if (level === "p3") {
      return travelExpenseToP3(area);
    }
    if (level === "p4") {
      return travelExpenseToP4(area);
    }
    if (level === "p5") {
      return travelExpenseToP5(area);
    }
}
 
function travelExpenseToP1(area) {
  let price = 200
  if (area === 1) {
    price += 200
  } else if (area === 2) {
    price += 100
  } else if (area === 3) {
    price += 50
  }
  return price;
}

function travelExpenseToP2() {
  //...
}

function travelExpenseToP3() {
   //...
}

function travelExpenseToP4() {
   //...
}

function travelExpenseToP5() {
   //...
}

在上面的代码中,我们把不同 p 级的处理逻辑独立成一个个函数,在 travelExpense 中的逻辑就会大大减少,由此我们在这里可以了解到这样的处理方式是符合“单一功能”原则的。

 
让我们接着来考虑,我们独立出来了每个 P 级的逻辑,但是你发现没有,虽然在 travelExpense 中代码精简了不少,但实际他的处理方式并没有发生改变,如果此时我们需要添加 M 级别,那么又要在 travelExpense 中新增逻辑,那么测试仍然要重测整个 travelExpense 函数的功能,这一点并没有达到一个理想的要求。

如何解决上面我们提到的问题呢?想要不改动 travelExpense 的逻辑,即使我们添加了新的职级和其对应的功能,也不会影响到原有的功能。

也许有部分同学已经想到了,我们可以用映射的方式来实现,看看下面这段代码:

const travelExpenseObj = {
  p1: function(area) {
    let price = 200
    if (area === 1) {
      price += 200
    } else if (area === 2) {
      price += 100
    } else if (area === 3) {
      price += 50
    }
    return price;
  },
  p2: function(area) {
    //...
  },
  p3: function(area) {
    //...
  },
  p4: function(area) {
    //...
  },
  p5: function(area) {
    //...
  },
};

function travelExpense(level, area) {
  return travelExpenseObj[level](area);
}

通过这样映射的方式,即使后面需要添加其他级别的情况,还是针对某个级别的修改,我们都只需要改动 travelExpenseObj 对象,并且不会对其他的级别和整体功能造成影响。

在上面的例子中,每个职级都有不同的处理逻辑,但是他们之间又有相似的地方,并且区分他们的也就只有职级这一个条件。因此,当面对这种情况时我们不得已去使用大量的 if … else 时,我们就首先需要封装他们的逻辑,随后再通过映射的方式来避免大量的条件判断,这也就是策略模式的应用场景。

到这里这一讲也到了尾声,在前面的文章中我也强调过,如果你遇到某一点没有办法很快的明白,不妨先放下,继续向后看。本章也是,如果你仍然不能理解,那就先继续看下去吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值