策略模式,一种广泛应用于各种情况的设计模式(设计模式与开发实践 P5)

文章目录

策略模式

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

例如,我们要计算年终奖,年终奖根据绩效 A、B、C 来计算最终数值

实现

最初我们很容易想到用 分支 if 来解决这个问题,如果绩效 = A 则工资 x 2,如果绩效 = B 则工资 x 3

如果经常使用这样的分支结构,你会发现代码耦合度很高,很容易就出现一大坨代码堆砌在一起,只是 x 2 或者 x 3 不足以形成难以维护的结构,但如果不是 x 2 而是一个复杂的代码块,我们显然会想到封装里面的代码!

javascript 复制代码
var performA = function (salary) {
  return salary * 4;
};

var performB = function (salary) {
  return salary * 3;
};

var performC = function (salary) {
  return salary * 2;
};

var calcBonus = function (level, salary) {
  if (level == "A") {
    return performA(salary);
  } else if (level == "B") {
    return performB(salary);
  } else if (level == "C") {
    return performC(salary);
  }
};

是的,虽然我们优化了代码,但没好到哪去,如果要添加一个 D 级,我们还是得堆砌代码

让我们来看看策略模式怎么做吧,策略模式让 策略 被定义和封装,且可以相互替换

这就是最终代码了,但在 javascript 中实现策略相较 C# 或者其他语言来说要容易的多,在下面举例了 C# 代码

javascript 复制代码
var strategies = {
  A: function (salary) {
    return salary * 4;
  },
  B: function (salary) {
    return salary * 3;
  },
  C: function (salary) {
    return salary * 2;
  },
};

var calculateBonus = function (level, salary) {
  return strategies[level](salary);
};

需要注意的是 strategies 对象存储的 3 个匿名函数, Func 类是用来存储函数的,需要一定的函数工具类基础

掌握这样的思想以后,试着把 {"A", (salary) => salary * 4} 解耦出去动态添加即可~

csharp 复制代码
using System;
using System.Collections.Generic;

public class Program
{
    private static Dictionary<string, Func<double, double>> strategies = new Dictionary<string, Func<double, double>>()
    {
        {"A", (salary) => salary * 4},
        {"B", (salary) => salary * 3},
        {"C", (salary) => salary * 2}
    };

    private static double CalculateBonus(string level, double salary)
    {
        return strategies[level](salary);
    }

    public static void Main(string[] args)
    {
        string level = "A";
        double salary = 1000;

        double bonus = CalculateBonus(level, salary);
        Console.WriteLine("Bonus: " + bonus);
    }
}

思想

通过上面的重构:

  • 消除了大片的分支语句
  • 计算奖金的逻辑不再存储在 CalculateBonus 里了,而是分布在策略对象里
  • 策略对象只负责计算奖金
  • 策略对象之间可以相互替换

实战 - 表单

这是一种尤为常见的表单验证方式,相信绝大多数前端程序员这样写过

显然能发现,这里的 if 堆砌过多,不仅如此,内部的 逻辑 相比上面的代码也更复杂

javascript 复制代码
var registerForm = function (form) {
  if (form.username.value === "") {
    alert("用户名不能为空");
    return false;
  }

  if (form.password.value.length < 6) {
    alert("密码长度不能少于6位");
    return false;
  }

  if (!/(^1[3|5|8][0-9]{9}$)/.test(form.phoneNumber.value)) {
    alert("手机号码格式不正确");
    return false;
  }
};

我们可以用策略模式的思路来实现类似这样的代码,这样当我们需要增加验证步骤时,只需要添加策略内容即可:

javascript 复制代码
var validateStrategy = {
  isNotEmpty: function (form) {
    if (form.name === "") {
      return "用户名不能为空";
    }
    return "";
  },
  minLength: function (form) {
    if (form.password.length < 6) {
      return "密码长度不能少于6位";
    }
    return "";
  },
  isMobile: function (form) {
    if (!/(^1[3|5|8][0-9]{9}$)/.test(form.phone)) {
      return "手机号码格式不正确";
    }
    return "";
  },
};

var validate = function (form) {
  for (let func in validateStrategy) {
    if (
      validateStrategy.hasOwnProperty(func) &&
      typeof validateStrategy[func] === "function"
    ) {
      var msg = validateStrategy[func](form);
      if (msg != "") return false;
    }
  }
  return true;
};
相关推荐
BD_Marathon2 小时前
设计模式——依赖倒转原则
java·开发语言·设计模式
BD_Marathon2 小时前
设计模式——里氏替换原则
java·设计模式·里氏替换原则
jmxwzy3 小时前
设计模式总结
设计模式
J_liaty19 小时前
23种设计模式一代理模式
设计模式·代理模式
程序员敲代码吗19 小时前
如何通过命令行启动COMSOL的参数化、批处理和集群扫描
java·c#·bash
苏渡苇1 天前
优雅应对异常,从“try-catch堆砌”到“设计驱动”
java·后端·设计模式·学习方法·责任链模式
短剑重铸之日1 天前
《设计模式》第十一篇:总结
java·后端·设计模式·总结
feasibility.1 天前
AI 编程助手进阶指南:从 Claude Code 到 OpenCode 的工程化经验总结
人工智能·经验分享·设计模式·自动化·agi·skills·opencode
BD_Marathon1 天前
七大设计原则介绍
设计模式
dingdingfish1 天前
Bash 学习 - 第1章:Introduction
bash·shell·programming·introduction