当前位置: 首页 > news >正文

JavaScript设计模式与开发实践系列之策略模式

本系列为《JavaScript设计模式与开发实践》(作者:曾探)学习总结,如想深入了解,请支持作者原版

策略模式

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

举个形象的例子,使用策略模式计算奖金

业务需求:

  • 绩效为S的人年终奖有4倍工资

  • 绩效为A的人年终奖有3倍工资

  • 绩效为B的人年终奖有2倍工资

财务部希望我们可以提供一段代码,方便他们计算员工的年终奖。

最初的代码实现

我们可以编写一个名为calculateBonus的函数来计算员工的奖金数额,这个函数需要传入两个参数:工资数额绩效等级。代码如下:

var calculateBonus = function(performanceLevel, salary) {
    if (performanceLevel === 'S') {
        return salary * 4;
    }
    if (performanceLevel === 'A') {
        return salary * 3;
    }
    if (performanceLevel === 'B') {
        return salary * 2;
    }
};

calculateBonus('B', 20000); //輸出:40000
calculateBonus('S', 6000); //輸出:24000

可以发现,这段代码非常简单,但是存在着显而易见的缺点。

  1. calculateBonus函数比较庞大,包含了很多if语句,这些语句需要覆盖所有的逻辑分支

  2. calculateBonus函数缺乏弹性,如果增加新的绩效等级,那我们必须深入calculateBonus内部,违反了开放-封闭原则

  3. 算法的复用性差,如果在程序的其他地方需要重用这些计算奖金的算法,我们的选择只有复制和粘贴

因此,我们要重构这段代码。

使用组合函数重构代码

我们把各种算法封装到一个个小函数里面:

var performanceS = function(salary) {
    return salary * 4;
}
var performanceA = function(salary) {
    return salary * 3;
}
var performanceB = function(salary) {
    return salary * 2;
}

var calculateBonus = function(performanceLevel, salary) {
    if (performanceLevel == 'S') {
        return performanceS(salary);
    }
    if (performanceLevel == 'A') {
        return performanceA(salary);
    }
    if (performanceLevel == 'B') {
        return performanceB(salary);
    }
};

calculateBonus('A', 10000);//輸出:30000

我們的程序得到了一定的改善,但我們依然沒有解決最重要的問題:calculateBonus函數有可能越來越龐大,而且在系統變化的時候缺乏彈性。

使用策略模式重構代碼

将不变的部分和变化的部分隔开是每个设计模式的主题,策略模式也不例外,策略模式的目的就是将算法的使用与算法的实现分离开来

一个基于策略模式的程序至少由两部分组成,第一部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。第二部分是环境类Context接受客户的请求,随后把请求委托给某一个策略类

现在我们用策略模式来重构上边的代码。

//我们先把每种绩效的计算规则封装在对应的策略类里
var porformanceS = function() {};
porformanceS.prototype.calculate = function(salary) {
    return salary * 4;
};
var porformanceA = function() {};
porformanceA.prototype.calculate = function(salary) {
    return salary * 3;
};
var porformanceB = function() {};
porformanceB.prototype.calculate = function(salary) {
    return salary * 2;
};
//接下来定义奖金类Bonus:
var Bonus = function() {
    this.salary = null;
    this.strategy = null;
};

Bonus.prototype.setSalary = function(salary) {
    this.salary = salary;
}
Bonus.prototype.setStrategy = function(strategy) {
    this.strategy = strategy;
}
Bonus.prototype.getBonus = function() {
    return this.strategy.calculate(this.salary);
}

在完成最終的代碼之前,我們再來回顧一下策略模式的思想

定義一系列的算法,把它們一個個封裝起來,並且使它們可以互相替換。

如果说的更详细一点,就是:定义一系列的算法,把它们各自封装成策略类,算法被封装在策略类内部的方法里。在客户对Context发起请求的时候,Context总是把请求委托给这些策略对象中的某一个进行计算。
我们继续完成刚才的代码:

var Bonus = new Bonus();
bonus.setSalary(1000);
bonus.setStrategy(new performanceS());
bonus.getBonus();

JavaScript版本的策略模式

上述代码是模拟了一些传统的面向对象语言的实现,实际上在JavaScript中,函数也是对象,所以更简单和直接的做法是把strategy直接定义为函数:

var strategies = {
    "S": function ( salary ){
        return salary * 4;
    },
    "A": function ( salary ){
        return salary * 3;
    },
    "B": function ( salary ){
        return salary * 2;
    }
};

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

总结

在JavaScript语言中,策略类往往被函数所替代,这是策略模式就成为了一种隐形的模式。尽管这样,彻底了解策略模式,也有助于我们明白使用函数的好处。

相关文章:

  • 颜色名列表
  • 设计模式简介
  • 获取坐标封装 getPos
  • 修改vsftp默认端口
  • struts2标签报错问题
  • freeipa
  • cordova plugin数据传递概要
  • 全球顶级技术博客(转)
  • Java使用代理发送Http请求
  • adb_相关目录
  • js api 实现钉钉免登
  • Vmware vSphere 6.0之ESXI安装配置
  • html5 postMessage解决跨域、跨窗口消息传递
  • 迷宫问题用‘图’求解
  • jdbcType与javaType的对应关系
  • .pyc 想到的一些问题
  • 「译」Node.js Streams 基础
  • 【React系列】如何构建React应用程序
  • 【附node操作实例】redis简明入门系列—字符串类型
  • css属性的继承、初识值、计算值、当前值、应用值
  • es的写入过程
  • GitUp, 你不可错过的秀外慧中的git工具
  • Hibernate最全面试题
  • Nacos系列:Nacos的Java SDK使用
  • python大佬养成计划----difflib模块
  • Python连接Oracle
  • Python实现BT种子转化为磁力链接【实战】
  • TypeScript实现数据结构(一)栈,队列,链表
  • 代理模式
  • 讲清楚之javascript作用域
  • 浅谈JavaScript的面向对象和它的封装、继承、多态
  • 扫描识别控件Dynamic Web TWAIN v12.2发布,改进SSL证书
  • 深度学习入门:10门免费线上课程推荐
  • 深入体验bash on windows,在windows上搭建原生的linux开发环境,酷!
  • media数据库操作,可以进行增删改查,实现回收站,隐私照片功能 SharedPreferences存储地址:
  • PostgreSQL之连接数修改
  • 数据可视化之下发图实践
  • 新年再起“裁员潮”,“钢铁侠”马斯克要一举裁掉SpaceX 600余名员工 ...
  • #1015 : KMP算法
  • #if 1...#endif
  • #stm32驱动外设模块总结w5500模块
  • #我与Java虚拟机的故事#连载05:Java虚拟机的修炼之道
  • (1)(1.13) SiK无线电高级配置(五)
  • (非本人原创)史记·柴静列传(r4笔记第65天)
  • (附源码)ssm考生评分系统 毕业设计 071114
  • (欧拉)openEuler系统添加网卡文件配置流程、(欧拉)openEuler系统手动配置ipv6地址流程、(欧拉)openEuler系统网络管理说明
  • (一)基于IDEA的JAVA基础10
  • (译) 理解 Elixir 中的宏 Macro, 第四部分:深入化
  • (原创) cocos2dx使用Curl连接网络(客户端)
  • (转)http协议
  • (转)用.Net的File控件上传文件的解决方案
  • .form文件_SSM框架文件上传篇
  • .MyFile@waifu.club.wis.mkp勒索病毒数据怎么处理|数据解密恢复
  • .NET : 在VS2008中计算代码度量值
  • .NET DevOps 接入指南 | 1. GitLab 安装