JavaScript模式——策略模式:告别if-else地狱,2个案例让你“策略”在握

JavaScript模式——策略模式:告别if-else地狱,2个案例让你“策略”在握

1. 策略模式是什么鬼?

简单说,策略模式就是帮你把一堆“算法”或者“规则”分别打包,让它们可以随时互换。就像你去旅行,可以选择飞机、火车、自驾,目的地一样,但方式随便换。这种模式的核心思想就是把“做什么”和“怎么做”分开

比如,你要计算年终奖,绩效S和绩效A的算法不一样,但它们都是“计算奖金”这件事。策略模式就让你把这些算法一个个封装好,想用哪个就用哪个。

2. 策略模式长啥样?

策略模式通常有两部分:

  • 策略对象:负责具体干活,比如不同的奖金算法、不同的动画缓动公式。
  • 环境对象:负责接收命令,然后把活派给某个策略。

听起来抽象?没关系,下面两个例子保你一看就懂。

3. 案例一:年终奖怎么算?

3.1 新手写法:if-else堆成山

刚入行的程序员可能会这么写:

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

这段代码简单粗暴,但问题也很明显:

  • 函数越来越胖,加个绩效C就得往里塞代码。
  • 算法写死在函数里,别的项目想用?只能复制粘贴。
  • 哪天绩效S改成5倍工资?得钻进这个函数里改,一不小心还可能影响别的逻辑。

3.2 稍微进步:函数拆分

把每个算法单独拎出来,稍微好看一点:

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

可那个if-else还是赖在那儿不走。

3.3 面向对象版:策略模式登场

我们把每种绩效包装成一个“策略类”:

var performanceS = function() {}; performanceS.prototype.calculate = function(salary) { return salary * 4; }; var performanceA = function() {}; performanceA.prototype.calculate = function(salary) { return salary * 3; }; var performanceB = function() {}; performanceB.prototype.calculate = function(salary) { return salary * 2; }; // 奖金类(环境角色) 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); }; // 用起来 var bonus = new Bonus(); bonus.setSalary(10000); bonus.setStrategy(new performanceS()); console.log(bonus.getBonus()); // 40000 bonus.setStrategy(new performanceA()); console.log(bonus.getBonus()); // 30000

现在,想加绩效C,只需要新建一个performanceC类,根本不用碰原来的代码——完美符合开放-封闭原则

3.4 JavaScript精简版:函数当策略

JavaScript可是函数一等公民啊,何必非得用类?直接把策略写成函数,塞进一个对象里,多清爽:

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); }; console.log(calculateBonus('S', 20000)); // 80000 console.log(calculateBonus('A', 10000)); // 30000

你看,没了那些啰嗦的类,代码干净得像刚洗过的盘子。

4. 多态:策略模式的隐形翅膀

策略模式之所以这么灵活,靠的是多态。不管是什么策略,只要它们有相同的“接口”(比如都有一个calculate方法),环境对象就能一视同仁地调用。在JavaScript里,甚至不需要显式的接口,函数参数对了就行。

比如下面的动画例子,所有缓动函数都接收(t, b, c, d)四个参数,环境类只管调用,至于具体怎么算,那是策略自己的事。这就是多态的魅力——对象们虽然长得不一样,但说起话来都一个调调

5. 案例二:让小球动起来——缓动动画

5.1 动画原理

动画其实就是快速播放一系列画面,每秒几十张,眼睛就被骗了。在网页里,我们用定时器一点点改变元素的位置,每一帧算好新的坐标,元素就“动”起来了。

5.2 缓动算法:各种运动配方

var tween = { linear: function(t, b, c, d) { return c * t / d + b; }, easeIn: function(t, b, c, d) { return c * (t /= d) * t + b; }, strongEaseIn: function(t, b, c, d) { return c * (t /= d) * t * t * t * t + b; }, strongEaseOut: function(t, b, c, d) { return c * ((t = t / d - 1) * t * t * t * t + 1) + b; }, sineaseIn: function(t, b, c, d) { return c * (t /= d) * t * t + b; }, sineaseOut: function(t, b, c, d) { return c * ((t = t / d - 1) * t * t + 1) + b; } };

5.3 动画类:用策略模式控制节奏

我们写一个Animate类,把缓动算法当作策略注入,想用什么运动,就传什么算法名:

var Animate = function(dom) { this.dom = dom; // 要运动的元素 this.startTime = 0; this.startPos = 0; this.endPos = 0; this.propertyName = null; // 比如 'left', 'top' this.easing = null; this.duration = null; }; Animate.prototype.start = function(propertyName, endPos, duration, easing) { this.startTime = +new Date(); this.startPos = this.dom.getBoundingClientRect()[propertyName]; this.propertyName = propertyName; this.endPos = endPos; this.duration = duration; this.easing = tween[easing]; var self = this; var timeId = setInterval(function() { if (self.step() === false) { clearInterval(timeId); } }, 19); }; Animate.prototype.step = function() { var t = +new Date(); if (t >= this.startTime + this.duration) { this.update(this.endPos); return false; } var pos = this.easing( t - this.startTime, this.startPos, this.endPos - this.startPos, this.duration ); this.update(pos); return true; }; Animate.prototype.update = function(pos) { this.dom.style[this.propertyName] = pos + 'px'; };

使用的时候,想怎么动就怎么动:

var div = document.getElementById('div'); var animate = new Animate(div); animate.start('left', 500, 1000, 'strongEaseOut'); // 缓慢加速然后冲出?

只需换一个算法名字,小球的运动轨迹就完全变了——策略模式让你轻松切换“运动配方”。

6. 策略模式的优点和缺点

优点

  • 干掉一堆if-else,代码瞬间清爽。
  • 算法独立,扩展方便,想加新规则?写个新策略就行。
  • 算法复用,哪里需要哪里搬。
  • 组合和委托代替继承,更灵活。

缺点

  • 多了不少策略类或对象,文件数量增加。
  • 使用者得知道有哪些策略可选,不然不知道怎么配。

7. JavaScript的一等函数:策略模式隐身了

在JavaScript里,函数本身就是对象,可以到处传。所以策略模式常常简化到你都认不出来:

var S = function(salary) { return salary * 4; }; var A = function(salary) { return salary * 3; }; var B = function(salary) { return salary * 2; }; var calculateBonus = function(func, salary) { return func(salary); }; calculateBonus(S, 10000); // 40000

你看,策略模式已经“隐形”了,但思想还在——函数即策略。

8. 总结

策略模式不是什么高深莫测的东西,它就是把“算法”或“规则”抽出来单独管理,让主程序只关心“做什么”,不操心“怎么做”。通过两个例子(年终奖、缓动动画),你应该已经感受到它的魅力了。

在JavaScript里,因为函数是一等公民,策略模式往往变得非常轻盈,甚至感觉不到它的存在。但不管形式如何,其核心思想——分离变化,封装行为——依然是我们写出好代码的指路明灯。

Read more

【C++】第二十七节—C++11(下) | 可变参数模版+新的类功能+STL中一些变化+包装器

【C++】第二十七节—C++11(下) | 可变参数模版+新的类功能+STL中一些变化+包装器

Hi,好久不见,我是云边有个稻草人,偶尔中二的C++领域博主与你分享专业知识U·ェ·U 《C++》本篇文章所属专栏—持续更新中—欢迎订阅~ 目录 五、可变参数模版 1. 基本语法及原理 2. 包扩展 方式一 方式二 3. empalce系列接口 六、新的类功能 1. 默认的移动构造和移动赋值 2. 成员变量声明时给缺省值 3. defult和delete 4. final与override 七、STL中一些变化 八、包装器 1. function 2. bind 正文开始—— 五、可变参数模版 1. 基本语法及原理 * C++11支持可变参数模板,也就是说支持可变数量参数的函数模板和类模板,可变数目的参数被称 为参数包,

By Ne0inhk
【JavaEE初阶】告别小白!Java IO 流读写 + 文件操作实战

【JavaEE初阶】告别小白!Java IO 流读写 + 文件操作实战

我的个人主页我的专栏:人工智能领域、java-数据结构、Javase、C语言,MySQL,JavaEE初阶,希望能帮助到大家!!!点赞👍收藏❤ 目录 * 一、先搞懂:文件和文件系统的基础认知 * 二、Java 中操作文件的“核心工具”:File 类 * 1. File 类的关键属性、构造和方法 * 2. File 类实操:从获取信息到创建删除 * (1)搞懂 get 系列方法:获取文件信息 * (2)创建与删除文件:createNewFile() 和 delete() * (3)创建目录:mkdir() 和 mkdirs() 的区别 * (4)文件重命名:renameTo() * 三、Java IO

By Ne0inhk
Java 大视界 -- Java+Flink CDC 构建实时数据同步系统:从 MySQL 到 Hive 全增量同步(443)

Java 大视界 -- Java+Flink CDC 构建实时数据同步系统:从 MySQL 到 Hive 全增量同步(443)

Java 大视界 -- Java+Flink CDC 构建实时数据同步系统:从 MySQL 到 Hive 全增量同步(443) * 引言: * 正文: * 一、 核心认知:Flink CDC 与全增量同步逻辑 * 1.1 Flink CDC 核心原理 * 1.1.1 与传统数据同步方案的对比(实战选型参考) * 1.2 全增量同步核心逻辑(MySQL→Hive) * 1.2.1 关键技术点(实战必关注,每个点都踩过坑) * 二、 环境准备:生产级环境配置(可直接复用) * 2.1 核心依赖配置(pom.xml)

By Ne0inhk