关于用设计模式刷 LeetCode 这件事

  • 时间:2020-04-24 20:46 作者:懒成铁 来源: 阅读:595
  • 扫一扫,手机访问
摘要:前言最近在过 《剑指Offer》 这本书上的题,尽量把每题的多种解法都自己捋一遍,在过到 面试题20. 表示数值的字符串 这一题的时候,Discuss 里有一个同学提出了 职责链模式 的解法,让人眼前一亮,另一方面是笔者最近刚用 职责链模式处理了少量问题,于是决定用 JavaScript 重构一版题

前言

最近在过 《剑指Offer》 这本书上的题,尽量把每题的多种解法都自己捋一遍,在过到 面试题20. 表示数值的字符串 这一题的时候,Discuss 里有一个同学提出了 职责链模式 的解法,让人眼前一亮,另一方面是笔者最近刚用 职责链模式处理了少量问题,于是决定用 JavaScript 重构一版题解,再熟习熟习这个设计模式。

什么是职责链模式

所谓职责链模式,是把要做的某一件事,交由一系列解决器依次解决,每个解决器只完成自己的职责,一旦完成之后就交由下一个解决器解决。

如何重构一版具备 JavaScript 特色的职责链模式

因为 JavaScript 中 “函数是一等公民”的特性,可以采取更函数式的写法来取代类式写法。

题目形容

请实现一个函数用来判断字符串能否表示数值(包括整数和小数)。例如,字符串"+100"、"5e2"、"-123"、"3.1416"、"0123"及"-1E-16"都表示数值,但"12e"、"1a3.14"、"1.2.3"、"+-5"及"12e+5.4"都不是。

解题

根据职责链模式的定义,我们要做的可以划分为以下这几步:;

  • 分析总共要做哪几件事,分别写出对应的解决器函数。
  • 编写一个驱动所有解决器函数的高级函数。

思路分析

根据题意,我们可以很快地知道,主要是要划分出整数校验器浮点数校验器科学计数法校验器。接着要考虑特判情况,所以还可以细分出一个空串校验器。考虑到字符串前后空格以及打头的正负号是合法的,可以分出空格的trimmer正负号的trimmer。最后还需要一个兜底的校验器,再经过以上所有校验器校验后,依然不合法时返回false。接下里我们一个一个实现就可。

兜底的校验器

这个是最好写的,直接封装一个函数返回 false 就可。

function falseValidator() {    return false}

空串校验器

先从简单的开始写,空串校验器,顾名思义,只需检测出是空串,则直接返回 false,否则交给下一个校验器解决。

/*** @param {string} value* @param {function} next*/function emptyValidator(value, next) {    return value.trim().length !== 0 ? next(value) : false}

整数校验器

这个也比较简单,遍历字符串,发现其中一个字符不是数字时,交给下一个解决器解决,否则返回 true

/*** @param {string} value* @param {function} next*/function integerValidator(value, next) {    for (const char of value) {      if (isNotNumber(char)) {        return next(value)      }    }    return true}/*** @param {string} value*/function isNotNumber(value) {    return isNaN(value) || value === "" || value === " "}

浮点数校验器

从这里开始就比较麻烦了,不过也有一个统一的思路,无论是浮点数还是科学计数法,他们都有一个分隔符,我们可以采取相似二分法的思想,分割后再对左右两个部分进行判断就可。

/*** @param {string} value* @param {function} next*/function floatValidator(value, next) {    const pos = value.indexOf(".")        if (pos === -1) {      return next(value)    }        const left = value.substring(0, pos)    const right = value.substring(pos + 1)        // 解决 .xxx 的情况    if (left === "") {      if (partFloatValidator(right)) {        return true      }          return next(value)    }        // 解决 xxx. 的情况    if (right === "") {      if (partFloatValidator(left)) {        return true      }          return next(value)    }        if (partFloatValidator(left) && partFloatValidator(right)) {      return true    }        return next(value)}

这里要注意 xxx..xxx,这两个特殊情况也是合法的。接下里二分之后也有一个解决器需要我们写,而且可以发现,这个解决器是一个复合解决器,所以我们可以先把驱动用的高级函数和需要的简单解决器都写出来了。

const partFloatValidator = process([    emptyValidator,    headTailIntegerValidator,    integerValidator,    falseValidator,])      /*** @param {function[]} validators*/function process(validators) {    return validators.reduceRight((next, validate) => (data) =>      validate(data, next)    )}/*** @param {string} value* @param {function} next*/function headTailIntegerValidator(value, next) {    if (isNotNumber(value[0]) || isNotNumber(value[value.length - 1])) {      return false    }        return next(value)}

科学计数法校验器

具体思路和浮点数校验器相同。

const leftpartSienceFormatValidator = process([    emptyValidator,    signTrimmer,    headTailSpaceValidator,    emptyValidator,    integerValidator,    floatValidator,    falseValidator,])const rightpartSienceFormatValidator = process([    emptyValidator,    signTrimmer,    headTailSpaceValidator,    emptyValidator,    integerValidator,    falseValidator,])/*** @param {string} value* @param {function} next*/function headTailSpaceValidator(value, next) {    if (value.startsWith(" ") || value.endsWith(" ")) {      return false    }        return next(value)}/*** @param {string} value* @param {function} next*/function spaceTrimmer(value, next) {    return next(value.trim())}/*** @param {string} value* @param {function} next*/function signTrimmer(value, next) {    if (value.startsWith("+") || value.startsWith("-")) {      value = value.substring(1)    }        return next(value)}/*** @param {string} value* @param {function} next*/function sienceFormatValidator(value, next) {    value = value.toLowerCase()        const pos = value.indexOf("e")        if (pos === -1) {      return next(value)    }        const left = value.substring(0, pos)    const right = value.substring(pos + 1)        if (      leftpartSienceFormatValidator(left) &&      rightpartSienceFormatValidator(right)    ) {      return true    }        return next(value)}

最终的校验器

通过写上述的两个复合校验器,最终的校验器也是同样的道理,只需按正确的判定顺序复合上述校验器就可。

const isNumber = process([    emptyValidator,    spaceTrimmer,    signTrimmer,    integerValidator,    floatValidator,    sienceFormatValidator,    falseValidator,])

总结

运用设计模式,尽管多写了很多代码,但是拓展性和可维护性是不可忽视的,缺点就是运行效率比较低,不过作为一种新思路,笔者还是很喜欢的。

参考资料

1.详细浅显的思路分析,多解法

  • 全部评论(0)
最新发布的资讯信息
【系统环境|】2FA验证器 验证码如何登录(2024-04-01 20:18)
【系统环境|】怎么做才能建设好外贸网站?(2023-12-20 10:05)
【系统环境|数据库】 潮玩宇宙游戏道具收集方法(2023-12-12 16:13)
【系统环境|】遥遥领先!青否数字人直播系统5.0发布,支持真人接管实时驱动!(2023-10-12 17:31)
【系统环境|服务器应用】克隆自己的数字人形象需要几步?(2023-09-20 17:13)
【系统环境|】Tiktok登录教程(2023-02-13 14:17)
【系统环境|】ZORRO佐罗软件安装教程及一键新机使用方法详细简介(2023-02-10 21:56)
【系统环境|】阿里云 centos 云盘扩容命令(2023-01-10 16:35)
【系统环境|】补单系统搭建补单源码搭建(2022-05-18 11:35)
【系统环境|服务器应用】高端显卡再度登上热搜,竟然是因为“断崖式”的降价(2022-04-12 19:47)
手机二维码手机访问领取大礼包
返回顶部