Skip to content

策略模式 #63

@wangjing013

Description

@wangjing013

掌握策略模式对于培养大家的良好的编程习惯和重构意识大有裨益.

下面来看看策略模式的定义:

定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换

策略模式目的:

策略模式的目的就是将算法的使用与算法的实现分离开来.

抛开概念, 来看一个真实场景问题.

支付方式

有一天产品经理牛哥找小明说,我们需要接入几种支付方式分别对应:

  • 支付宝
  • 京东
  • 微信

小明思索了一下, 心里并有了实现方式. 便找后端拿到对应支付方式标识对应如下:

支付宝: 1701
京东: 1702
微信: 1703

接着仔细读完接入文档,进入相应的开发. 大概的实现如下:

const alipayId = 1701;
const jdpayId = 1702;
const wechatId = 1703;

function toPay(params){
  if(alipayId === params.payType) {
    console.log("调用支付宝")
  }else if(jdpayId === params.payType) {
    console.log("调用京东")
  }else if(wechatId === params.payType) {
    console.log("调用微信")
  }else {
    console.log("未找到对应支付方式")
  }
}

这基本上能满足我们的功能层面的要求, 但是这种实现有没有问题 ?

  • 违法单一原则,所有支付方式的实现都放在一起,随着版本迭代代码体积也会是非常的庞大且难以维护.
  • 复用性差,其它地方需要复用其中一些支付方法,显然现在写法不能满足的.
  • 缺乏弹性,假设又新增一种买鸭支付方式,需要深入到toPay 内部,这同样违反了开发封闭原则.

问题多不可怕,重点去改善它.

使用组合函数来重构代码

把每个支付方式的实现进行提取(extract), 如下.

function aliPay(){
  console.log("调用支付宝")
}

function jdPay(){
  console.log("调用京东")
}

function wechatPay(){
  console.log("调用微信")
}

function toPay(params){
  if(alipayId === params.payType) {
    aliPay();
  }else if(jdpayId === params.payType) {
    jdPay();
  }else if(wechatId === params.payType) {
    wechatPay();
  }else {
    console.log("未找到对应支付方式")
  }
}

OK, 现在每个函数都只负责一件事,同时支付方式的复用性得到提高.

虽然经过上面的改造后在一定程度上改善了我们代码,但是依然没有解决最重要的问题: toPay 函数体积可能会越来越大的问题, 而且当系统发生变化的时候缺乏弹性.

先来捋一捋 toPay 做的事情, 主要接收用户请求,将请求分发给不同支付方式并执行.

在第一步改造主要围绕着执行阶段, 那么剩下的就要从 分发 入手.

开放封闭改造

由于业务的需求,现在需要添加一种新的支付方式买鸭分期, 小明想了想我这么做就好了,如下:

function aliPay(){
  console.log("调用支付宝")
}

function jdPay(){
  console.log("调用京东")
}

function wechatPay(){
  console.log("调用微信")
}
function maiyaPay(){
  console.log("调用买鸭")
}

function toPay(params){
  if(alipayId === params.payType) {
    aliPay();
  }else if(jdpayId === params.payType) {
    jdPay();
  }else if(wechatId === params.payType) {
    wechatPay();
  }else if(maiyaId === params.payType) {
    maiyaPay();
  }else {
    console.log("未找到对应支付方式")
  }
}

上面是新增 maiyaPay 方法,然后在 toPay 中添加 if-else 分支,这应该是大家常用的解决方案. 这样其实还是在修改 toPay 的函数体, 没有实现对扩展开放、对修改封闭的效果.

那我该怎么做? 其实仔细想一想, toPayif-else 作用就是把支付方式与处理函数进行匹配. 那么在JS中有没有既能明确映射他们之间的关系,又不损失其灵活性的方法? 对象映射

现在我们通过对象方式来改进它.

首先把所有支付方式收集到一个对象中:

const alipayId = 1701;
const jdpayId = 1702;
const wechatId = 1703;
const maiyaId = 1704;

const strategies = {
  [alipayId]: (){
    console.log("调用支付宝")
  },
  [jdpayId]: (){
    console.log("调用京东")
  },
  [wechatId]: (){
    console.log("调用微信")
  },
  [maiyaId]: (){
    console.log("调用买鸭")
  },
}

当我们使用某个支付方式的时,可以通过支付方式 payType 去获取即可:

function toPay(params){
  const payMethod =  strategies[params.payType];
  payMethod(params)
}

这样一来, if-else 被消除了, 同样后面新增支付方式,我们只需要在 strategies 中新增即可.

现在回顾前面的定义 定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. 算法可以理解就是每种支付方式处理函数. 概念里面的“相互替换”指的是“多态”. 这里可以不用去关心.

接着看看策略模式的目的将算法的使用与算法的实现分离开来. 再仔细回顾我们实现是不是就是这样的.

任何场景下遇到类似问题, 那么应该想一想有没有更好的实现方式, 不要只用 if-else 一把梭了.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions