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

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

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

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

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

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

具体规则如下:

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

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

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

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

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

function getPrice(originalPrice, status) {
    if (status === "pre-sale") {
        return originalPrice * 0.8;
    }

    if (status === "promotion") {
        if (origialPrice <= 100) {
            return origialPrice * 0.9;
        } else {
            return originalPrice - 20;
        }
    }

    if (status === "default") {
        return originalPrice;
    }
}

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

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

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

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

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

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

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

function getPrice(originalPrice, status) {
    if (status === "pre-sale") {
        return originalPrice * 0.8;
    }

    if (status === "promotion") {
        if (origialPrice <= 100) {
            return origialPrice * 0.9;
        } else {
            return originalPrice - 20;
        }
    }
    // 黑色星期五规则
    if (status === "black-friday") {
        if (origialPrice >= 100 && originalPrice < 200) {
            return origialPrice - 20;
        } else if (originalPrice >= 200) {
            return originalPrice - 50;
        } else {
            return originalPrice * 0.8;
        }
    }

    if (status === "default") {
        return originalPrice;
    }
}

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

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

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

/**
 * 预售商品价格规则
 * @param {*} origialPrice
 * @returns
 */
function preSalePrice(origialPrice) {
    return originalPrice * 0.8;
}
/**
 * 促销商品价格规则
 * @param {*} origialPrice
 * @returns
 */
function promotionPrice(origialPrice) {
    if (origialPrice <= 100) {
        return origialPrice * 0.9;
    } else {
        return originalPrice - 20;
    }
}
/**
 * 黑色星期五促销规则
 * @param {*} origialPrice
 * @returns
 */
function blackFridayPrice(origialPrice) {
    if (origialPrice >= 100 && originalPrice < 200) {
        return origialPrice - 20;
    } else if (originalPrice >= 200) {
        return originalPrice - 50;
    } else {
        return originalPrice * 0.8;
    }
}
/**
 * 默认商品价格
 * @param {*} origialPrice
 * @returns
 */
function defaultPrice(origialPrice) {
    return origialPrice;
}

function getPrice(originalPrice, status) {
    if (status === "pre-sale") {
        return preSalePrice(originalPrice);
    }

    if (status === "promotion") {
        return promotionPrice(originalPrice);
    }

    if (status === "black-friday") {
        return blackFridayPrice(originalPrice);
    }

    if (status === "default") {
        return defaultPrice(originalPrice);
    }
}

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

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

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

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

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

const priceStrategies = {
    "pre-sale": preSalePrice,
    promotion: promotionPrice,
    "black-friday": blackFridayPrice,
    default: defaultPrice,
};

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

function getPrice(originalPrice, status) {
    return priceStrategies[status](originalPrice);
}

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

之前的代码逻辑如下:

优化后的代码逻辑如下:

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

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

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

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

总结

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

JavaScript设计模式之策略模式详解的更多相关文章

  1. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  2. HTML5数字输入仅接受整数的实现代码

    这篇文章主要介绍了HTML5数字输入仅接受整数的实现代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  3. amaze ui 的使用详细教程

    这篇文章主要介绍了amaze ui 的使用详细教程,本文通过多种方法给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  4. html5简介_动力节点Java学院整理

    这篇文章主要介绍了html5简介,用于指定构建网页的元素,这些元素中的大多数都用于描述网页内容,有兴趣的可以了解一下

  5. ios 8 Homescreen webapp,关闭和打开iPad停止javascript

    我有一个适用于iPad的全屏HTML5网络应用程序,并且刚刚安装了IOS8来试用它,它一切正常,直到你关闭并重新启动iPad.一旦web应用程序重新启动javascript就会停止并加载新页面不会重新启动它.在iPad上的Safari中打开同一页面时,关闭和打开iPad会继续按预期工作.其他人注意到了这个或想出了一个解决方案吗?解决方法这似乎是我在iOS8.1.1更新中解决的.

  6. iOS 6 javascript与object.defineProperty的间歇性问题

    当访问使用较新的Object.defineProperty语法定义属性的对象的属性时,有没有其他人注意到新iOS6javascript引擎中的间歇性错误/问题?https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty我正在看到javascript失败的情况,说

  7. ios – 如何使用JSExport导出内部类的方法

    解决方法似乎没有办法将内部类函数导出到javascript.我将内部类移出并创建了独立的类,它起作用了.

  8. 静音iOS推送通知与React Native应用程序在后台

    我有一个ReactNative应用程序,我试图获得一个发送到JavaScript处理程序的静默iOS推送通知.我看到的行为是AppDelegate中的didReceiveRemoteNotification函数被调用,但是我的JavaScript中的处理程序不会被调用,除非应用程序在前台,或者最近才被关闭.我很困惑的事情显然是应用程序正在被唤醒,并且它的didReceiveRemoteNotifi

  9. ios – 内存泄漏与UIWebView和Javascript

    清楚地包含一个Javascript文件到我的HTML是使UIWebView泄漏内存.当我重复使用相同的UIWebView对象时,或者每当我有内容实例化一个新的漏洞时,会出现泄漏的事实,导致我认为必须有一些JavaScript文件被loadHTMLString处理,导致泄漏.有人知道如何解决这个问题吗?

  10. iOS应用程序的UI自动化测试如何与乐器和Javascript

    从WWDC2010视频会议中了解iOS应用程序的自动化UI测试,但没有实践.从代码项目project,我们可以有一个例子.这个问题在这里听到有涉及这个的人.任何限制?解决方法我建议从AlexWollmer开始使用thisblogpost.他创建了一个非常有用的JavaScript库:tuneup_jswithtest()函数,它允许测试分离和有用的帮助者以及为自动化仪器编写测试的断言.

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部