经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » 设计模式 » 查看文章
JavaScript设计模式之策略模式详解
来源:jb51  时间:2022/6/27 10:53:28  对本文有异议

什么是设计模式?为什么需要学习设计模式?

学习设计模式的目的是:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

经典的设计模式有 23 种,但并不是每一种设计模式都被频繁使用。在这里,介绍最常用和最实用的几种设计模式,本文先来介绍策略模式(Strategy Pattern)。

策略模式是一种行为设计模式,定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。

假如正在开发一个在线商城的项目,每个产品都有原价,称之为 originalPrice。但实际上并非所有产品都以原价出售,可能会推出允许以折扣价出售商品的促销活动。

商家可以在后台为产品设置不同的状态,然后实际售价将根据产品状态和原价动态调整。

具体规则如下:

  • 部分产品已预售:为鼓励客户预订,将在原价基础上享受 20% 的折扣。
  • 部分产品处于正常促销阶段:如果原价低于或等于100,则以10%的折扣出售;如果原价高于 100,则减 10 元。
  • 有些产品没有任何促销活动:它们属于 default 状态,并以原价出售。

这时需要写一个获取商品价格的函数 getPrice ,应该怎么写呢?

  1. function getPrice(originalPrice, status) {
  2. // ...
  3. // 返回价格;
  4. }

事实上,面对这样的问题,如果不考虑任何设计模式,最直观的写法可能 if-else 多次条件判断语句来计算价格。

有三种状态,可以快速编写如下代码:

  1. function getPrice(originalPrice, status) {
  2. if (status === "pre-sale") {
  3. return originalPrice * 0.8;
  4. }
  5.  
  6. if (status === "promotion") {
  7. if (origialPrice <= 100) {
  8. return origialPrice * 0.9;
  9. } else {
  10. return originalPrice - 20;
  11. }
  12. }
  13.  
  14. if (status === "default") {
  15. return originalPrice;
  16. }
  17. }

有三个条件,上面的代码写了三个 if 语句,这是非常直观的代码,但是这段代码组织上不好。

首先,它违反了单一职责原则(Single responsibility principle,规定每个类或者函数都应该有一个单一的功能,并且该功能应该由这个类或者函数完全封装起来)。函数 getPrice 做了太多的事情,这个函数不易阅读,也容易出现 bug 。如果一个条件出现 bug ,整个函数就会崩溃。同时,这样的代码也不容易调试。

并且这段代码很难应对变化的需求,这时就需要考虑设计模式,其往往会在业务逻辑发生变化时展现出它的魅力。

假设业务扩大了,现在还有另一个折扣促销:黑色星期五。折扣规则如下:

  • 价格低于或等于 100 元的产品以 20% 的折扣出售。
  • 价格高于 100 元但低于 200 元的产品将减少 20 元。
  • 价格高于或等于 200 元的产品将减少 20 元。

这个时候该怎么扩展 getPrice 函数呢?

看起来必须在 getPrice 函数中添加一个条件语句:

  1. function getPrice(originalPrice, status) {
  2. if (status === "pre-sale") {
  3. return originalPrice * 0.8;
  4. }
  5.  
  6. if (status === "promotion") {
  7. if (origialPrice <= 100) {
  8. return origialPrice * 0.9;
  9. } else {
  10. return originalPrice - 20;
  11. }
  12. }
  13. // 黑色星期五规则
  14. if (status === "black-friday") {
  15. if (origialPrice >= 100 && originalPrice < 200) {
  16. return origialPrice - 20;
  17. } else if (originalPrice >= 200) {
  18. return originalPrice - 50;
  19. } else {
  20. return originalPrice * 0.8;
  21. }
  22. }
  23.  
  24. if (status === "default") {
  25. return originalPrice;
  26. }
  27. }

每当增加或减少折扣时,都需要更改函数。这种做法违反了开闭原则(对扩展开放,对修改关闭)。修改已有的功能很容易出现新的错误,而且还会使得 getPrice 越来越臃肿。

那么如何优化这段代码呢?

首先,可以拆分这个函数 getPrice 以减少臃肿。

  1. /**
  2. * 预售商品价格规则
  3. * @param {*} origialPrice
  4. * @returns
  5. */
  6. function preSalePrice(origialPrice) {
  7. return originalPrice * 0.8;
  8. }
  9. /**
  10. * 促销商品价格规则
  11. * @param {*} origialPrice
  12. * @returns
  13. */
  14. function promotionPrice(origialPrice) {
  15. if (origialPrice <= 100) {
  16. return origialPrice * 0.9;
  17. } else {
  18. return originalPrice - 20;
  19. }
  20. }
  21. /**
  22. * 黑色星期五促销规则
  23. * @param {*} origialPrice
  24. * @returns
  25. */
  26. function blackFridayPrice(origialPrice) {
  27. if (origialPrice >= 100 && originalPrice < 200) {
  28. return origialPrice - 20;
  29. } else if (originalPrice >= 200) {
  30. return originalPrice - 50;
  31. } else {
  32. return originalPrice * 0.8;
  33. }
  34. }
  35. /**
  36. * 默认商品价格
  37. * @param {*} origialPrice
  38. * @returns
  39. */
  40. function defaultPrice(origialPrice) {
  41. return origialPrice;
  42. }
  43.  
  44. function getPrice(originalPrice, status) {
  45. if (status === "pre-sale") {
  46. return preSalePrice(originalPrice);
  47. }
  48.  
  49. if (status === "promotion") {
  50. return promotionPrice(originalPrice);
  51. }
  52.  
  53. if (status === "black-friday") {
  54. return blackFridayPrice(originalPrice);
  55. }
  56.  
  57. if (status === "default") {
  58. return defaultPrice(originalPrice);
  59. }
  60. }

经过这次修改,虽然代码行数增加了,但是可读性有了明显的提升。getPrice 函数显然没有那么臃肿,写单元测试也比较方便。

但是上面的改动并没有解决根本的问题:代码还是充满了 if-else ,而且当增加或者减少折扣规则的时候,仍然需要修改 getPrice

其实使用这些 if-else 的目的就是为了对应状态和折扣策略。

从图中可以发现,这个逻辑本质上是一种映射关系:产品状态与折扣策略的映射关系。

可以使用映射而不是冗长的 if-else 来存储映射,按照这个思路可以构造一个价格策略的映射关系(策略名称与其处理函数之间的映射),如下:

  1. const priceStrategies = {
  2. "pre-sale": preSalePrice,
  3. promotion: promotionPrice,
  4. "black-friday": blackFridayPrice,
  5. default: defaultPrice,
  6. };

将状态与折扣策略结合起来,价格函数就可以优化成如下:

  1. function getPrice(originalPrice, status) {
  2. return priceStrategies[status](originalPrice);
  3. }

这时候如果需要加减折扣策略,不需要修改函数,只需要修改价格策略映射关系 priceStrategies

之前的代码逻辑如下:

优化后的代码逻辑如下:

以上的优化策略就是使用了设计模式之策略模式,在实际的项目开发过程中还是比较实用。

在什么情况下可以考虑使用策略模式呢?如果函数具有以下特征:

  • 判断条件很多
  • 各个判断条件下的代码相互独立

然后可以将每个判断条件下的代码封装成一个独立的函数,然后建立判断条件和具体策略的映射关系。

总结

到此这篇关于JavaScript设计模式之策略模式的文章就介绍到这了,更多相关JavaScript策略模式内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号