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

2023-09-16 01:38:38

策略模式

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

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

实现

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

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

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# 代码

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} 解耦出去动态添加即可~

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 堆砌过多,不仅如此,内部的 逻辑 相比上面的代码也更复杂

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;
  }
};

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

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;
};

更多推荐

【漏洞复现】易思智能物流无人值守系统文件上传

本文由掌控安全学院-江月投稿【产品介绍】易思无人值守智能物流系统是一款集成了人工智能、机器人技术和物联网技术的创新产品。它能够自主完成货物存储、检索、分拣、装载以及配送等物流作业,帮助企业实现无人值守的智能物流运营,提高效率、降低成本,为现代物流行业带来新的发展机遇。【漏洞描述】易思无人值守智能物流系统/Sys_Rep

h5下载文件,无兼容问题~

最近写了个页面,打开页面出现文件列表,用户可以下载文件。失败方案使用a标签进行下载,参考代码如下:因为有批量下载的需求,这里将xhr请求单独封装到downloadFile.js中//downloadFile.jsconstdownloadFile=(url,onProgress,xhrAr)=>{console.log

Linux知识点 -- 网络基础(二)-- 应用层

Linux知识点–网络基础(二)--应用层文章目录Linux知识点--网络基础(二)--应用层一、使用协议来实现一个网络版的计算器1.自定义协议2.守护进程3.使用json来完成序列化二、HTTP协议1.概念2.HTTP协议请求和响应的报文格式3.使用HTTP协议进行网络通信4.HTTP协议的方法5.HTTP协议的状态

postgresql完整备份,增量备份,差异备份详细说明及对比(InsCode AI 创作助手)

postgresql完整备份,增量备份,差异备份详细说明及对比PostgreSQL是一款开源的关系型数据库管理系统,为了确保数据的安全性和可恢复性,数据库备份是至关重要的。在这篇博客中,我们将深入探讨PostgreSQL备份策略,包括完整备份、增量备份和差异备份,以及它们之间的比较。此外,我们还将提供相应的备份和恢复示

代理IP与Socks5代理:跨界电商智能爬虫的引擎与安全壁垒

一、引言跨界电商已成为全球商业发展的重要趋势,但要成功进入多样化的市场,企业需要大量的市场数据和对隐私安全的保障。代理IP和Socks5代理是两项关键技术,它们在这一领域的应用对于企业的成功至关重要。二、代理IP:跨界电商的智能数据引擎多地区数据采集:代理IP能够模拟不同地区的IP地址,帮助企业轻松采集多地区的市场数据

对IP协议概念以及IP地址的概念进行简单整理

网络层重要协议参考模型和协议栈IP协议IPv4数据报IP数据报格式IPv4地址特殊IP地址私有IP地址和公有IP地址子网划分参考模型和协议栈IP协议IP协议定义了网络层数据传送的基本单元,也制定了一系列关于网络层的规则。IPv4数据报网络层的协议数据单元PDU叫做分组;网络层的传输单位叫做数据报。协议数据单元PDU是对

【Java】泛型 之 擦拭法

泛型是一种类似”模板代码“的技术,不同语言的泛型实现方式不一定相同。Java语言的泛型实现方式是擦拭法(TypeErasure)。所谓擦拭法是指,虚拟机对泛型其实一无所知,所有的工作都是编译器做的。例如,我们编写了一个泛型类Pair<T>,这是编译器看到的代码:publicclassPair<T>{privateTfi

分布式系统的 38 个知识点

天天说分布式分布式,那么我们是否知道什么是分布式,分布式会遇到什么问题,有哪些理论支撑,有哪些经典的应对方案,业界是如何设计并保证分布式系统的高可用呢?1.架构设计这一节将从一些经典的开源系统架构设计出发,来看一下,如何设计一个高质量的分布式系统;而一般的设计出发点,无外乎冗余:简单理解为找个备胎,现任挂掉之后,备胎顶

Layui快速入门之第十一节 弹出层

目录一:基本用法API打开弹层基础属性弹出信息框弹出询问框弹出提示框弹出加载层弹出贴士层弹出输入框弹出图片层弹出标签层关闭弹层关闭所有层关闭最近一次打开的层2.8+全局配置默认属性样式初始化就绪重新设置弹层样式设置弹层的标题获取iframe页中的元素在iframe页中获取弹层索引设置iframe层高度自适应重新设置if

go-GC垃圾回收

GCGC是自动化内存管理回收机制虚拟内存函数栈的数据是会根据函数返回而自动销毁的,而堆上的数据是不会随着函数自动销毁的,堆内数据会随着程序运行而逐渐变大,从而导致内存OOM,Go语言就用了GC来清理堆上的内存数据。如何区分垃圾堆上内存数据不在被栈上和数据段上的引用变量所指向,那就一定是内存垃圾。这个原理称为"可达性",

PostgreSql 统一修改date字段为timestamp

在《Powdersigner+PostgreSql同步表结构到pg数据库》中,导入表结构到pg数据后,发下时间对不上了。mysql的datetime转换后pg的变成了date了。再同步到数据后,就变成日期类型了。因为表中基本都有创建时间和修改时间,两个相对固定的字段,因此想做一个统一的处理。把《postgreSql使用

热文推荐