一文聊透策略模式

简介: 设计模式系列之策略模式

你好呀,我是Bella酱~

今天我们来一起聊聊策略模式,主要是以下几个方面:定义、生活中的例子、核心组成、UML图、代码实现、适用场景等。

定义

什么是策略模式呢?

引用GoF著作的《设计模式:可复用面向对象软件的基础》中的一段话:

定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

什么意思呢?就是说有N个算法,它们要实现的目的是相同的,所以各算法之间是可以互相替换的,每个客户端独立选择使用哪一个算法,不同的客户端之间可以选择不同的算法。

生活中的例子

其实在我们的日常生活中就有很多使用策略模式的例子呢。

例如大多数工作日我们都要做的一个事情,使用某一种交通方式从家去公司。具体使用哪种交通方式呢?根据家到公司的距离,不同的同学会选择不同的交通方式,例如搭乘地铁、自驾、骑行、步行等等。

无论是哪种交通方式,我们的目的是相同的--从家去公司。各种交通方式是独立的,而且是可以互相替换的。每一位同学又会根据个人的实际情况来选择不同的交通方式。这不就是典型的策略模式的例子吗?

核心组成

策略模式主要由4部分组成:

  1. Strategy策略接口。该接口定义了一个方法,各具体策略类会实现该方法。
  2. ConcreteStrategy具体策略。ConcreteStrategy有很多个,它们都实现了Strategy策略接口中定义的方法。
  3. Context上下文。Context中定义了策略类,并且定义了一个方法,该方法在执行时会执行各具体策略中的方法。
  4. Client客户端。Client客户端中会创建Context上下文,并且指定具体策略类,然后再执行Strategy策略接口中定义的方法。

以上文中的例子为例:

  1. Strategy策略接口中定义了一个方法:选择从家去公司的交通方式。
  2. ConcreteStrategy具体策略,例如地铁策略、自驾策略、骑行策略等等,均实现了“选择从家去公司的交通方式”这一方法,但是各具体策略之间的方法体是不同的。
  3. Context上下文中定义了Strategy策略类,同时Context上下文中还有一个方法,该方法会执行Strategy策略接口中定义的方法。
  4. Client客户端中创建Context上下文,并且指定用哪种具体策略,例如指定使用骑行策略,然后调用Context中定义的方法来执行策略中的方法。

UML图

以上文中的例子为例,策略模式的UML图如下:

对UML类图有疑惑的同学可以看下这篇文章:看懂UML图

策略模式UML图.jpg

代码实现

我们来一起看下如何用代码实现上述例子吧,so cool~

首先定义一个Strategy策略接口。

package com.bella.design.patterns.strategy;

/**
 * 策略类
 *
 * @author Bella酱
 * @date 2021/08/08
 */
public interface Strategy {
    /**
     * 选择从家去公司的交通方式
     *
     * @param home    家
     * @param company 公司
     * @return 交通方式
     */
    String selectTransportation(String home, String company);
}

其次定义3个具体策略,分别为SubwayStrategy、CarStrategy、BikeStrategy。
SubwayStrategy地铁策略。

package com.bella.design.patterns.strategy;

import java.text.MessageFormat;

/**
 * 地铁策略
 *
 * @author Bella酱
 * @date 2021/08/08
 */
public class SubwayStrategy implements Strategy {

    /**
     * 选择从家去公司的交通方式
     *
     * @param home    家
     * @param company 公司
     * @return 交通方式
     */
    public String selectTransportation(String home, String company) {
        System.out.println(
                MessageFormat.format("家在-{0}, 公司在-{1},建议您乘坐地铁出行!", home, company));
        return "Subway";
    }
}

CarStrategy自驾策略。

package com.bella.design.patterns.strategy;

import java.text.MessageFormat;

/**
 * 自驾策略
 *
 * @author Bella酱
 * @date 2021/08/08
 */
public class CarStrategy implements Strategy {
    /**
     * 选择从家去公司的交通方式
     *
     * @param home    家
     * @param company 公司
     * @return 交通方式
     */
    public String selectTransportation(String home, String company) {
        System.out.println(
                MessageFormat.format("家在-{0}, 公司在-{1},安全带已系好,秋名山车神准备出发啦!", home, company));
        return "Car";
    }
}

BikeStrategy骑行策略。

package com.bella.design.patterns.strategy;

import java.text.MessageFormat;

/**
 * 骑行策略
 *
 * @author Bella酱
 * @date 2021/08/08
 */
public class BikeStrategy implements Strategy {
    /**
     * 选择从家去公司的交通方式
     *
     * @param home    家
     * @param company 公司
     * @return 交通方式
     */
    public String selectTransportation(String home, String company) {
        System.out.println(
                MessageFormat.format("家在-{0}, 公司在-{1},绿色出行,为环保贡献一份力量!", home, company));
        return "Bike";
    }
}

然后再定义Context上下文。

package com.bella.design.patterns.strategy;

/**
 * 上下文
 *
 * @author Bella酱
 * @date 2021/08/08
 */
public class Context {
    /**
     * 策略
     */
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    /**
     * 选择去公司的交通方式
     *
     * @param home
     * @param company
     * @return
     */
    public String selectTransToCompany(String home, String company) {
        return strategy.selectTransportation(home, company);
    }
}

最后再来一个测试类测下效果~

package com.bella.design.patterns.strategy;

/**
 * 策略模式Test
 *
 * @author Bella酱
 * @date 2021/08/08
 */
public class StrategyTest {
    public static void main(String[] args) {
        Context strategyContext = new Context(new SubwayStrategy());
        String trans = strategyContext.selectTransToCompany("京杭大运河", "未来科技城");
        System.out.println(trans);

        strategyContext = new Context(new CarStrategy());
        trans = strategyContext.selectTransToCompany("老余杭", "未来科技城");
        System.out.println(trans);

        strategyContext = new Context(new BikeStrategy());
        trans = strategyContext.selectTransToCompany("未来科技城", "未来科技城");
        System.out.println(trans);
    }
}

run一下StrategyTest,结果如下:

StrategyTest运行结果.jpg

诺,就是我们想要的效果啦!

适用场景

通过上面的讲解,相信你已经掌握策略模式的使用啦,但是我们应该什么时候使用策略模式呢?

  1. 当一个行为因不同的条件要执行不同的方法体时。其实就是我们平常见到的if else啦。对于项目中的if else语句,可以考虑下是否可以定义不同的具体策略类,然后将else中的语句移入到各个ConcreteStrategy中哦!
  2. 当许多相似的类,仅仅是行为上有差异时。这些相似的类是不是就像各个不同的ConcreteStrategy呢?把那些相似的类利用策略模式重构一波后,代码结构会更加清晰,可读性也会提高很多!
  3. 当对于一个算法,已有几种不同的实现方式,而且后续还可能会增加新的实现方式时。用策略模式,可以在你要新增算法的实现方式时,不修改已有算法的实现,大大降低了引入新策略的风险。

好啦,我们今天的文章就到这里啦,文中涉及的代码都可以从 https://github.com/lilinru/design-patterns 这里下载哦,我们下期见~

相关文章
|
存储 前端开发 JavaScript
实现鼠标悬停显示书名、作者和价格的悬浮提示框功能
实现鼠标悬停显示书名、作者和价格的悬浮提示框功能
|
缓存 安全 Java
为什么全局变量可能成为多线程环境中的安全隐患
为什么全局变量可能成为多线程环境中的安全隐患
|
4月前
|
人工智能 自然语言处理 JavaScript
用 LLM 辅助性能测试报告生成
性能测试报告通常包含测试概述、方案说明、结果分析、问题定位、优化建议及上线评估等内容。报告编写面临数据分析复杂、撰写耗时、经验依赖等问题。引入大型语言模型(LLM),可实现报告智能生成,提升效率与专业度。LLM具备自然语言生成、数据归纳、专家知识迁移等能力,可适配多格式、多语言输出。通过构建LLM辅助的报告生成引擎,结合Prompt设计,可高效输出结构化报告。实践表明,LLM在测试结论总结、瓶颈分析与优化建议方面表现优异,为性能测试智能化升级提供有力支撑。
310 0
|
API Docker 容器
SenseVoice实现语音转文字
这篇文章介绍了如何使用SenseVoice实现语音转文字的功能,包括通过Docker部署服务、使用网页界面或API进行语音文件的转换,并提供了详细的部署与使用步骤。
2372 1
SenseVoice实现语音转文字
|
安全 Linux 数据安全/隐私保护
详解如何登录Docker Registry
【8月更文挑战第24天】
1476 0
|
Python
微信虚拟聊天对话生成器示例
python实现对话生成器代码示例
1049 0
|
前端开发 JavaScript Java
【日志显示】vue实现前端展示后端带颜色的日志
vue实现后端日志到前端展示中能带颜色。
2904 0
【日志显示】vue实现前端展示后端带颜色的日志
|
设计模式 缓存 搜索推荐
高德信息业务DDD实战 - 聊聊用领域重构胶水代码
本文记录了搞得信息业务DDD实战中如何用领域重构代码
高德信息业务DDD实战 - 聊聊用领域重构胶水代码
|
存储
我们常听到Kbps、Mbps、Gbps它们分别的含义?
我们总听的有些单位Kbps、Mbps、Gbps,但有的人可能搞不懂Kbps、Mbps、Gbps代表什么意思?那我们今天就简单的介绍下它们分别的含义吧。先诠释下Bps,即Bits per Second的缩写,是数据传输速度的常见单位。
26257 0
|
Java Maven 微服务
【微服务项目异常】Maven中模块显示灰色的原因与解决办法
【微服务项目异常】Maven中模块显示灰色的原因与解决办法
556 0