面试官:策略模式有使用过吗?我:没有......(上)

简介: 面试官:策略模式有使用过吗?我:没有......(上)

面试官:策略模式有使用过吗?我:没有......

何为策略模式?

  • • 比如在业务逻辑或程序设计中比如要实现某个功能,有多种方案可供我们选择。比如要压缩一个文件,我们既可以选择 ZIP 算法,也可以选择 GZIP 算法。
  • • 这些算法灵活多样,可随意切换,而这种解决方案就是我们所要学习的策略模式。

定义或概念

  • 策略模式:定义一系列的算法,将他们一个个封装,并使他们可相互替换。

策略模式的最佳实践

例子1:奖金计算

  • • 题目:在很多公司的年终奖都是按照员工的工资基数和年底绩效情况来发放的,例如,绩效为 S 的人年终奖有 4 倍工资,A 的人年终奖有 3 倍,B 的人年终奖有 2 倍。要求我们写出一个程序来更快的计算员工的年终奖。(编写一个名为 calcBonus 方法来计算每个员工的奖金数额)
  • • 可能有些人一上来直接就在一个方法中进行很多 if...else 或 switch...case 判断, 然后通过这个方法进行计算。我们可以来试着写一下:
/**
 *
 * @param {*} level 绩效等级
 * @param {*} salary 工资基数
 * @returns 年终奖金额
 */
var calcBonus = function (level, salary) {
    if (level === "S") {
        return salary * 4;
    } else if (level === "A") {
        return salary * 3;
    } else if (level === "B") {
        return salary * 2;
    }
};
calcBonus('A', 20000); // 60000
calcBonus('B', 8000); // 16000
  • • 我想在我们每个人初学代码时肯定都写出过这样的代码。其实这段代码有显而易见的缺点:
  1. 1. calcBonus 函数逻辑太多
  2. 2. calcBonus 函数缺乏弹性,比如如果我们需要增加一个等级 C,那就必须要去修改 calcBonus 函数。这就违反了开放-封闭原则。
  3. 3. 复用性差。如果后续还要重用这个程序去计算奖金,我们只有去 C,V。
  • • 此时,可能会想对 calcBonus 函数进行封装,如我们使用组合函数的形式,如下:
var totalS = function (salary) {
    return salary * 4;
};
var totalA = function (salary) {
    return salary * 3;
};
var totalB = function (salary) {
    return salary * 2;
};
var calcBonus = function (level, salary) {
    if (level === "S") {
        return totalS(salary);
    } else if (level === "A") {
        return totalA(salary);
    } else if (level === "B") {
        return totalB(salary);
    }
};
calcBonus('A', 20000); // 60000
calcBonus('B', 8000); // 16000
  • • 这样,我们将程序进行了进一步改善,但改善微乎其微,依旧没有解决最重要的问题,calcBonus 函数还是有可能会很庞大,并且也没有弹性。
  • • 那我们再将它进行一次改造,使用策略模式:将其定义为一系列的算法,将他们每一个封装起来,将不变的部分和变化的部分隔开。
  • • 在这段程序中,算法的使用方式是不变的,都是根据某个算法获取最后的奖金金额。而在每个算法的内部实现却是不同的,每一个等级对应着不同的计算规则
  • • 而在策略模式程序中:最少由两部分组成,一部分是一组策略类,在策略类中封装了具体的算法,并负责具体的计算过程。一部分是环境类 context,接受用户的请求,并将请求委托给某一个策略类。
  • • 如下:
var strategies = {
    S: function (salary) {
        return salary * 4;
    },
    A: function (salary) {
        return salary * 3;
    },
    B: function (salary) {
        return salary * 2;
    },
};
var calcBonus = function (level, salary) {
    return strategies[level](salary);
}
calcBonus('A', 20000); // 60000
calcBonus('B', 8000); // 16000

• 其实,策略模式的实现并不复杂,关键是如何从策略模式的实现背后,找到封装变化,委托和多态性这些思想的价值

例子2:表单验证

  • • 题目:在 Web 开发中,表单校验是一个常见的话题,要求使用策略模式来完成表单验证。
  • • 比如:
  1. 1. 用户名不能为空
  2. 2. 密码长度不能少于 6 位
  3. 3. 手机号码必须符合正确格式
  • • 让我们来实现一下吧:
function submit() {
    let { username, password, tel } = infoForm;
    if (username === "") {
        Toast("用户名不能为空");
        return false;
    }
    if (password.length < 6) {
        Toast("密码不能少于 6 位");
        return false;
    }
    if (!/(^1[3|5|8][0-9]{9}$)/.test(tel)) {
        Toast("手机号码格式不正确");
        return false;
    }
    // .....
}
  • • 这是我们常见的实现方式,它的缺点跟计算奖金一例类似:
  1. 1. submit 函数庞大,包含了很多 if...else 语句
  2. 2. submit 函数缺乏弹性,如果对其新加一些新的校验规则,如果我们把密码长度从 6 改到 8.那我们就必须要改动 submit 函数,否则无法实现该校验。这也是违反开放-封闭原则。
  3. 3. 复用差,如果说我们程序中还有另一个表达需要验证,也是进行类似的校验,那我们可能会进行 C, V 操作。
  • • 使用策略模式来进行重构
let infoForm = {
    username: "我是某某某",
    password: 'zxcvbnm',
    tel: 16826384655,
};
var strategies = {
    isEmpty: function (val, msg) {
        if (!val) return msg;
    },
    minLength: function (val, length, msg) {
        if (val.length < length) return msg;
    },
    isTel: function (val, msg) {
        if (!/(^1[3|5|8][0-9]{9}$)/.test(val)) return msg;
    },
};
var validFn = function () {
    var validator = new Validator();
    let { username, password, tel } = infoForm;
    validator.add(username, "isEmpty", "用户名不能为空");
    validator.add(password, "minLength:6", "密码不能少于 6 位");
    validator.add(tel, "isTel", "手机号码格式不正确");
    var msg = validator.start();
    return msg;
};
class Validator {
    constructor() {
        this.cache = [];
    }
    add(attr, rule, msg) {
        var ruleArr = rule.split(":");
        this.cache.push(function () {
            var strategy = ruleArr.shift();
            ruleArr.unshift(attr);
            ruleArr.push(msg);
            return strategies[strategy].apply(attr, ruleArr);
        });
    }
    start() {
        for (let i = 0; i < this.cache.length; i++) {
            var msg = this.cache[i]();
            if (msg) return msg;
        }
    }
}
function submit() {
    let msg = validFn();
    if (msg) {
        Toast(msg);
        return false;
    }
    console.log('verify success');
    // .....
}
submit();
目录
相关文章
|
算法 Java 编译器
java.lang.StackOverflowError解决方案
java.lang.StackOverflowError解决方案
459 3
|
SQL 消息中间件 监控
实时计算 Flink版产品使用问题之怎么使用Metric Reporters监控作业
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
存储 Python
链表中插入节点
链表中插入节点
|
9月前
|
JSON 安全 API
微店item_search_shop-获得店铺的所有商品API接口设计指南
本文介绍如何设计高效、安全且易用的item_search_shop API接口,用于微店商品检索和管理。关键需求包括数据完整性、高并发支持、安全性及易用性。开发者需在微店开放平台注册获取API凭证,并通过Access Token调用接口。接口支持一次性获取店铺所有商品信息,提供Python示例代码。注意事项涵盖凭证安全、异常处理和数据准确性。此API助力商家提升电商运营效率。
|
9月前
|
Java 关系型数据库 数据库
京东面试:聊聊Spring事务?Spring事务的10种失效场景?加入型传播和嵌套型传播有什么区别?
45岁老架构师尼恩分享了Spring事务的核心知识点,包括事务的两种管理方式(编程式和声明式)、@Transactional注解的五大属性(transactionManager、propagation、isolation、timeout、readOnly、rollbackFor)、事务的七种传播行为、事务隔离级别及其与数据库隔离级别的关系,以及Spring事务的10种失效场景。尼恩还强调了面试中如何给出高质量答案,推荐阅读《尼恩Java面试宝典PDF》以提升面试表现。更多技术资料可在公众号【技术自由圈】获取。
|
存储 安全 编译器
【C++】类的六大默认成员函数及其特性(万字详解)
【C++】类的六大默认成员函数及其特性(万字详解)
176 3
|
11月前
|
安全 Shell Linux
内网渗透测试基础——Windows PowerShell篇
内网渗透测试基础——Windows PowerShell篇
451 0
|
弹性计算 固态存储 大数据
阿里云服务器多少钱一年?2024年阿里云服务器价格表曝光!
2024年最新阿里云服务器租用费用优惠价格表,轻量2核2G3M带宽轻量服务器一年82元,折合6.8元1个月,新老用户同享99元一年服务器,2核4G5M服务器ECS优惠价199元一年,2核4G4M轻量服务器298元一年,2核4G服务器30元3个月,4核16G10M服务器26元1个月、149元半年,8核32G服务器90元1个月、271元3个月,阿小云整理阿里云服务器租用费用价格表,包括一年优惠价格、一个月和1小时收费明细表
1105 3
|
安全 编译器 C++
C++一分钟之-泛型Lambda表达式
【7月更文挑战第16天】C++14引入泛型lambda,允许lambda接受任意类型参数,如`[](auto a, auto b) { return a + b; }`。但这也带来类型推导失败、隐式转换和模板参数推导等问题。要避免这些问题,可以明确类型约束、限制隐式转换或显式指定模板参数。示例中,`safeAdd` lambda使用`static_assert`确保只对算术类型执行,展示了一种安全使用泛型lambda的方法。
179 1
|
SQL 数据库 Java
HQL vs SQL:谁将统治数据库查询的未来?揭秘Hibernate的神秘力量!
【8月更文挑战第31天】Hibernate查询语言(HQL)是一种面向对象的查询语言,它模仿了SQL的语法,但操作对象为持久化类及其属性,而非数据库表和列。HQL具有类型安全、易于维护等优点,支持面向对象的高级特性,内置大量函数,可灵活处理查询结果。下面通过示例对比HQL与SQL,展示HQL在实际应用中的优势。例如,HQL查询“从员工表中筛选年龄大于30岁的员工”只需简单地表示为 `FROM Employee e WHERE e.age &gt; 30`,而在SQL中则需明确指定表名和列名。此外,HQL在处理关联查询时也更为直观易懂。然而,对于某些复杂的数据库操作,SQL仍有其独特优势。
249 0