23种设计模式之策略模式

策略模式详解:模式简介、动机、结构及应用

  • 一、策略模式简介
    • 1.1 定义
    • 1.2 模式类型
    • 1.3 主要作用
    • 1.4 优点
    • 1.5 缺点
  • 二、模式动机
  • 三、模式结构
  • 四、策略模式的实现
    • 4.1 步骤一:定义策略接口
    • 4.2 步骤二:实现具体策略类
    • 4.3 步骤三:创建上下文类
    • 4.4 步骤四:客户端切换策略
    • 4.5 动态设置策略
  • 五、应用场景
    • 5.1 排序算法
    • 5.2 支付方式
    • 5.3 数据压缩
    • 5.4 游戏AI行为
  • 六、策略模式的变体与扩展
    • 6.1 内联策略
    • 6.2 参数化策略
    • 6.3 缓存策略
  • 七、总结:rocket::rocket::rocket:

一、策略模式简介

在这里插入图片描述

1.1 定义


  策略模式,又称政策模式,是行为型设计模式之一。其定义如下:

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

1.2 模式类型


  行为型

1.3 主要作用


  • 封装算法:将每一个算法封装成对象,使得算法可以互相替换和独立扩展。
  • 消除条件分支语句:避免在客户端代码中使用大量的条件分支语句。通过策略模式,条件分支语句被算法的多态性替代。
  • 行为的独立变化:可以在不影响客户端的情况下,增加新的算法或修改已有算法,增强系统的灵活性和可扩展性。

1.4 优点


  • 开放-关闭原则:策略模式通过策略接口的多态性实现,符合开放-封闭原则,可以在不修改客户端代码的情况下,增加新的策略。
  • 消除条件语句:避免了在客户端代码中使用大量的条件语句,将算法的选择委托给策略对象,更加清晰、简介。
  • 提高可扩展性:通过为不同算法定义独特的策略类,可以方便地扩展新的算法而不会影响现有系统。
  • 更好的代码组织:算法的实现被封装在独立的策略类中,代码组织更好,易于理解和维护。

1.5 缺点


  • 略微增加复杂性:由于引入了策略接口和具体的策略类,增加了类的数量和系统的复杂性。
  • 客户端的意识增强:客户端必须了解所有的策略类,并知道在何种情况下使用哪一个策略,增加客户端的负担。

二、模式动机


在这里插入图片描述

  在实际开发中,常常遇到对某种行为或算法进行替换或扩展的需求例如,一个图形界面程序需要对不同的绘图算法进行切换、一个电商系统需要支持多种不同的支付方式、游戏开发中需要不同的AI行为策略等等。对于这些需求,通常的做法是使用大量的条件分支语句来实现不同的行为:

if (condition1) {
    // Algorithm A
} else if (condition2) {
    // Algorithm B
} else if (condition3) {
    // Algorithm C
}

  然而,随着需求的不断增加,这种实现方式会导致代码冗长复杂且不易维护,违背了开闭原则。策略模式的动机就是为了解决这一问题,通过将算法封装为独立的策略类,实现算法的易扩展高可维护性

三、模式结构


  策略模式的关键在于:算法被分离为独立的策略类,各类策略实现一个共同的策略接口,这个接口可以故定义策略的各种行为方法。在上下文环境(Context)类中持有策略接口的引用,客户通过设置具体的策略对象来进行算法的切换。

  • Context(上下文):持有一个策略对象的引用,它会把客户端的请求委托给策略对象。
  • Strategy(抽象策略):策略的共同接口或抽象类。
  • ConcreteStrategy(具体策略):具体实现了某种算法的策略类。

Java代码示例

以下是策略模式的示例代码,用于展示策略模式在现实场景中的应用。

// 策略接口
interface Strategy {
    void executeAlgorithm();
}

// 具体策略类A
class ConcreteStrategyA implements Strategy {
    public void executeAlgorithm() {
        System.out.println("执行策略A算法");
    }
}

// 具体策略类B
class ConcreteStrategyB implements Strategy {
    public void executeAlgorithm() {
        System.out.println("执行策略B算法");
    }
}

// 上下文类
class Context {
    private Strategy strategy;

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public void executeStrategy() {
        strategy.executeAlgorithm();
    }
}

// 客户端代码
public class StrategyPatternDemo {
    public static void main(String[] args) {
        Context context = new Context();
        
        // 设置为策略A
        context.setStrategy(new ConcreteStrategyA());
        context.executeStrategy();
        
        // 设置为策略B
        context.setStrategy(new ConcreteStrategyB());
        context.executeStrategy();
    }
}

  在上述代码中,我们定义了一个策略接口和两个具体策略类,通过在上下文类中设置具体的策略对象,客户端可以方便地切换不同的算法。

四、策略模式的实现

4.1 步骤一:定义策略接口


  策略接口是所有策略类的共同父接口,它定义了所有策略类必须实现的方法。以下是策略接口的定义:

interface Strategy {
    void executeAlgorithm();
}

4.2 步骤二:实现具体策略类


  每一个具体策略类都实现了策略接口,并包含了具体的算法实现。以下是两个具体策略类的实现:

class ConcreteStrategyA implements Strategy {
    public void executeAlgorithm() {
        System.out.println("执行策略A算法");
    }
}
class ConcreteStrategyB implements Strategy {
    public void executeAlgorithm() {
        System.out.println("执行策略B算法");
    }
}

4.3 步骤三:创建上下文类


  上下文类持有一个策略对象引用,并通过该引用来调用具体的算法。以下是上下文类的定义和实现:

class Context {
    private Strategy strategy;

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public void executeStrategy() {
        strategy.executeAlgorithm();
    }
}

4.4 步骤四:客户端切换策略


  在客户端代码中,通过设置具体的策略对象来切换不同的算法:

public class StrategyPatternDemo {
    public static void main(String[] args) {
        Context context = new Context();
        
        // 设置为策略A,执行策略A算法
        context.setStrategy(new ConcreteStrategyA());
        context.executeStrategy();
        
        // 设置为策略B,执行策略B算法
        context.setStrategy(new ConcreteStrategyB());
        context.executeStrategy();
    }
}

4.5 动态设置策略


  策略模式的强大之处在于可以动态地设置策略,而不仅仅是在初始化时设置。通过上下文类的 setStrategy 方法,客户端可以根据需求在运行时动态切换策略,以得到不同的行为

五、应用场景


在这里插入图片描述

  策略模式在许多实际场景中都有应用,它特别适合处理那些存在多种行为或算法且需要在运行时动态切换的情况。以下是一些常见的应用场景:

5.1 排序算法


  在排序程序中,可能需要支持多种不同的排序算法,如快速排序、归并排序、插入排序等。通过策略模式,可以将每一种排序算法封装成独立的策略类,然后在运行时根据需要选择具体的排序算法。

// 排序策略接口
interface SortStrategy {
    void sort(int[] array);
}

// 快速排序策略
class QuickSortStrategy implements SortStrategy {
    public void sort(int[] array) {
        // 快速排序算法实现
    }
}

// 归并排序策略
class MergeSortStrategy implements SortStrategy {
    public void sort(int[] array) {
        // 归并排序算法实现
    }
}

// 排序上下文类
class SortContext {
    private SortStrategy strategy;

    public void setStrategy(SortStrategy strategy) {
        this.strategy = strategy;
    }

    public void sortArray(int[] array) {
        strategy.sort(array);
    }
}

// 客户端
public class SortClient {
    public static void main(String[] args) {
        SortContext context = new SortContext();
        int[] array = {5, 3, 8, 6, 2};

        // 使用快速排序
        context.setStrategy(new QuickSortStrategy());
        context.sortArray(array);
        
        // 使用归并排序
        context.setStrategy(new MergeSortStrategy());
        context.sortArray(array);
    }
}

5.2 支付方式


  在电子商务系统中,用户可能需要选择不同的支付方式,如信用卡支付、支付宝支付、微信支付等。通过策略模式,可以将每种支付方式封装成独立的策略类,用户可以在运行时选择具体的支付方式。

// 支付策略接口
interface PaymentStrategy {
    void pay(double amount);
}

// 信用卡支付策略
class CreditCardPaymentStrategy implements PaymentStrategy {
    public void pay(double amount) {
        System.out.println("使用信用卡支付" + amount + "元");
    }
}

// 支付宝支付策略
class AlipayPaymentStrategy implements PaymentStrategy {
    public void pay(double amount) {
        System.out.println("使用支付宝支付" + amount + "元");
    }
}

// 支付上下文类
class PaymentContext {
    private PaymentStrategy strategy;

    public void setStrategy(PaymentStrategy strategy) {
        this.strategy = strategy;
    }

    public void makePayment(double amount) {
        strategy.pay(amount);
    }
}

// 客户端
public class PaymentClient {
    public static void main(String[] args) {
        PaymentContext context = new PaymentContext();
        
        // 使用信用卡支付
        context.setStrategy(new CreditCardPaymentStrategy());
        context.makePayment(100.0);
        
        // 使用支付宝支付
        context.setStrategy(new AlipayPaymentStrategy());
        context.makePayment(200.0);
    }
}

5.3 数据压缩


  在数据压缩程序中,可能需要使用不同的压缩算法,如ZIP、RAR、TAR等。通过策略模式,可以将每种压缩算法封装成独立的策略类,然后在运行时选择具体的压缩算法。

// 压缩策略接口
interface CompressionStrategy {
    void compress(String data);
}

// ZIP压缩策略
class ZipCompressionStrategy implements CompressionStrategy {
    public void compress(String data) {
        System.out.println("使用ZIP压缩数据:" + data);
    }
}

// RAR压缩策略
class RarCompressionStrategy implements CompressionStrategy {
    public void compress(String data) {
        System.out.println("使用RAR压缩数据:" + data);
    }
}

// 压缩上下文类
class CompressionContext {
    private CompressionStrategy strategy;

    public void setStrategy(CompressionStrategy strategy) {
        this.strategy = strategy;
    }

    public void compressData(String data) {
        strategy.compress(data);
    }
}

// 客户端
public class CompressionClient {
    public static void main(String[] args) {
        CompressionContext context = new CompressionContext();
        
        // 使用ZIP压缩
        context.setStrategy(new ZipCompressionStrategy());
        context.compressData("示例数据");
        
        // 使用RAR压缩
        context.setStrategy(new RarCompressionStrategy());
        context.compressData("示例数据");
    }
}

5.4 游戏AI行为


  在游戏开发中,非玩家角色(NPC)的行为策略可以根据不同的情境进行切换,例如攻击行为、防守行为、巡逻行为等。通过策略模式,可以将每种行为封装成独立的策略类,游戏引擎可以根据游戏情境动态切换不同的行为。

// 行为策略接口
interface BehaviorStrategy {
    void executeBehavior();
}

// 攻击行为策略
class AttackBehaviorStrategy implements BehaviorStrategy {
    public void executeBehavior() {
        System.out.println("执行攻击行为");
    }
}

// 防守行为策略
class DefenseBehaviorStrategy implements BehaviorStrategy {
    public void executeBehavior() {
        System.out.println("执行防守行为");
    }
}

// 巡逻行为策略
class PatrolBehaviorStrategy implements BehaviorStrategy {
    public void executeBehavior() {
        System.out.println("执行巡逻行为");
    }
}

// AI行为上下文类
class AIContext {
    private BehaviorStrategy strategy;

    public void setStrategy(BehaviorStrategy strategy) {
        this.strategy = strategy;
    }

    public void executeCurrentBehavior() {
        strategy.executeBehavior();
    }
}

// 客户端
public class GameClient {
    public static void main(String[] args) {
        AIContext context = new AIContext();
        
        // 执行攻击行为
        context.setStrategy(new AttackBehaviorStrategy());
        context.executeCurrentBehavior();
        
        // 执行防守行为
        context.setStrategy(new DefenseBehaviorStrategy());
        context.executeCurrentBehavior();
        
        // 执行巡逻行为
        context.setStrategy(new PatrolBehaviorStrategy());
        context.executeCurrentBehavior();
    }
}

六、策略模式的变体与扩展

6.1 内联策略


  在某些场景中,可能不需要定义独立的策略类,可以通过内联的方式在上下文类中直接定义策略。内联策略适用于简单的场景,但不建议使用在复杂的应用中。

class Context {
    private Strategy strategy;

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public void executeStrategy() {
        strategy.executeAlgorithm();
    }
}

// 客户端
public class StrategyPatternDemo {
    public static void main(String[] args) {
        Context context = new Context();
        
        // 设置为策略A,执行策略A算法
        context.setStrategy(() -> System.out.println("执行策略A算法"));
        context.executeStrategy();
        
        // 设置为策略B,执行策略B算法
        context.setStrategy(() -> System.out.println("执行策略B算法"));
        context.executeStrategy();
    }
}

6.2 参数化策略


  策略模式还可以通过参数化来增强灵活性,使得具体策略类可以接受多个参数来配置其行为。

interface ParameterizedStrategy {
    void executeAlgorithm(String parameter);
}

class ConcreteParameterizedStrategyA implements ParameterizedStrategy {
    public void executeAlgorithm(String parameter) {
        System.out.println("执行策略A算法,参数:" + parameter);
    }
}

class ConcreteParameterizedStrategyB implements ParameterizedStrategy {
    public void executeAlgorithm(String parameter) {
        System.out.println("执行策略B算法,参数:" + parameter);
    }
}

class ParameterizedContext {
    private ParameterizedStrategy strategy;

    public void setStrategy(ParameterizedStrategy strategy) {
        this.strategy = strategy;
    }

    public void executeStrategy(String parameter) {
        strategy.executeAlgorithm(parameter);
    }
}

// 客户端
public class ParameterizedStrategyDemo {
    public static void main(String[] args) {
        ParameterizedContext context = new ParameterizedContext();
        
        // 使用参数化策略A
        context.setStrategy(new ConcreteParameterizedStrategyA());
        context.executeStrategy("参数1");
        
        // 使用参数化策略B
        context.setStrategy(new ConcreteParameterizedStrategyB());
        context.executeStrategy("参数2");
    }
}

6.3 缓存策略

在某些应用中,选择策略的过程可能比较耗时,通过缓存策略对象可以提高性能。可以使用工厂模式或单例模式来创建并缓存策略对象。

class StrategyFactory {
    private static final Map<String, Strategy> strategies = new HashMap<>();
    
    static {
        strategies.put("A", new ConcreteStrategyA());
        strategies.put("B", new ConcreteStrategyB());
    }

    public static Strategy getStrategy(String type) {
        return strategies.get(type);
    }
}

// 客户端
public class CachedStrategyDemo {
    public static void main(String[] args) {
        Context context = new Context();
        
        // 从工厂获取并使用策略A
        context.setStrategy(StrategyFactory.getStrategy("A"));
        context.executeStrategy();
        
        // 从工厂获取并使用策略B
        context.setStrategy(StrategyFactory.getStrategy("B"));
        context.executeStrategy();
    }
}

七、总结🚀🚀🚀


  策略模式作为一种行为型设计模式,通过将算法封装成独立的策略类,实现算法的易于扩展和高可维护性,避免了大量的条件分支语句,使得代码更加清晰、简洁。策略模式在排序算法、支付方式、数据压缩、游戏AI行为等场景中广泛应用。

  然而,策略模式引入了额外的类和接口,可能略微增加系统的复杂性,且客户端需要了解所有的策略类,增加了客户端的负担。开发人员在使用策略模式时需要权衡利弊,根据具体情况选择合适的设计方案。

  希望本篇关于策略模式的详细解析,能帮助开发人员更好地理解和应用这一设计模式,提高代码质量和系统的扩展性。如果有任何问题或建议,欢迎在评论区留言讨论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/651085.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

智能未来,触手可及,畅享移动云

目录 一、简介 二、移动云强大优势 1. 强大的网络基础设施 2. 可靠服务 3. 丰富产品线 4. 技术应用 5. 优惠价格策略 三、多商对比 网络优势 四、移动云的未来发展 五、实战应用 5.1 服务器选购 ​编辑5.2 服务器启动 5.3 实例操作 六、移动云的服务优势 6.1 客…

AppInventor2 代码调试方法,99%的人可能不知道这种方式!

AppInventor2 基本上所有调试App的前提是安装好AI伴侣&#xff0c;并与AI伴侣连接后进行&#xff0c;我们在设计界面的每次改动&#xff0c;都会实时的反映在AI伴侣中。 AI伴侣可以运行在手机上&#xff0c;也可以是安卓模拟器。 AI伴侣具体的安装及连接步骤请参考&#xff1…

Redisson集成SpringBoot

前言&#xff1a;Redisson集成SpringBoot主要有两种方式&#xff0c;一个是使用redisson-spring-boot-starter依赖&#xff08;优先推荐&#xff09;&#xff0c;毕竟springboot主打的就是约定大于配置&#xff0c;这个依赖就是为springboot准备的。 再一种方式就是引入rediss…

openflow协议抓包分析

1、准备实验拓扑&#xff1a; 在Mininet环境中创建一个简单的SDN拓扑&#xff0c;包括控制器、交换机、主机等。 确保拓扑能够正常运行&#xff0c;SDN交换机与控制器建立连接。 采用主机Ubuntu22.04主机&#xff0c;IP地址是192.168.87.130&#xff0c;安装opendaylight控制…

数据集007:垃圾分类数据集(含数据集下载链接)

数据集简介 本数据拥有 训练集&#xff1a;43685张&#xff1b; 验证集&#xff1a;5363张&#xff1b; 测试集&#xff1a;5363张&#xff1b; 总类别数&#xff1a;158类。 部分代码&#xff1a; 定义数据集 class MyDataset(Dataset):def __init__(self, modetrain, …

大模型PEFT(一)之推理实践学习记录

1. 简介 多种模型: LLaMA、Mistral、Mixtral-MoE、Qwen、Yi、Gemmha、Baichuan、ChatGLM、Phi等等。集成方法:(增量)预训练、指令监督微调、奖励模型训练、PPO训练和DPO训练。多种精度:32比特全参数微调、16比特冻结微调、16比特LORA微调和基于AQLM/AWQ/GPTQ/LLM.int8 的2/4/8…

使用 Elastic AI assistant for Observability 来分析日志

在今天的文章中&#xff0c;我们来参考之前的文章 “Elastic AI Assistant for Observability 和 Microsoft Azure OpenAI 入门” 来使用 Elastic AI assistant 分析日志。在本文章中&#xff0c;我们不使用 Azure clould。这样我们之间来进入主题&#xff0c;以免失去注意力。…

基于Zynq 7000 SoC的迁移设计

基于Zynq 7000 SoC的迁移设计 Vivado IDE工具使用IP集成器进行嵌入式开发。各种IP Vivado IDE IP目录中提供&#xff0c;以适应复杂的设计。您也可以添加 自定义IP到IP目录。 您可以将基于Zynq 7000平台处理器的设计迁移到Vivado design Suite中 使用以下步骤。 1.生成系统基础…

模型实战(20)之 yolov8分类模型训练自己的数据集

yolov8分类模型训练自己的数据集 yolov8,一个实时快速的端到端的集检测、分割、分类、姿态识别于一体的视觉算法库/框架本文将给出yolov8 分类模型的数据集制作格式及训练流程 1. 环境搭建 关于虚拟环境的搭建真的是老生常谈了,给出一个简单的搭建流程吧#新建虚拟环境 conda …

Linux 如何用上次的checkpoint文件dist_train.sh 接着训练【mmdetection】

在Linux环境下&#xff0c;如果你想要用上一次的checkpoint文件继续训练&#xff0c;你可以在你的dist_train.sh脚本中设置--resume_from参数。这个参数指定了checkpoint文件的路径&#xff0c;训练会从该文件的状态继续进行。 例如&#xff0c;如果你的checkpoint文件名为las…

【吊打面试官系列】Java高并发篇 - ConcurrentHashMap 的并发度是什么?

大家好&#xff0c;我是锋哥。今天分享关于 【ConcurrentHashMap 的并发度是什么?】面试题&#xff0c;希望对大家有帮助&#xff1b; ConcurrentHashMap 的并发度是什么? ConcurrentHashMap 的并发度就是 segment 的大小&#xff0c;默认为 16&#xff0c; 这意味着最多同时…

考公事业编

一天时间就能考完。上午行测 &#xff0c;下午申论。 省考国考基本一模一样。 行测基本都是客观的题目。都是选择题。 130道选择。很多计算都是初中题目。 申论是主观题。 行测申论 行测&#xff1a;

【Android14 ShellTransitions】(四)Transition收集动画参与者

这一节的内容在WMCore中&#xff0c;现在Transition已经走到COLLECTING状态了&#xff0c;并且可以收集动画参与者了。 那么Transition是在什么时候去收集动画参与者&#xff1f;回到我们最初的ActivityStarter.startActivityUnchecked&#xff1a; 在调用了TransitionControl…

vb.net打开CAD指指定路径文件

首先打开vsto,创建窗体&#xff0c;添加一个按钮&#xff0c;双击按钮录入代码&#xff1a; Public Class Form1Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.ClickDim cad As Objectcad CreateObject("autocad.Application")cad…

代码随想录算法训练营第六天| 242.有效的字母异位词 、349. 两个数组的交集 、202. 快乐数、1. 两数之和

242.有效的字母异位词 题目链接&#xff1a; 203.移除链表元素 文档讲解&#xff1a;代码随想录 状态&#xff1a;so easy 思路&#xff1a;使用计数器数组统计字符串中字符出现频率。 题解 public boolean isAnagram(String s, String t) {// 将输入的字符串s转换为字符数组…

World Creator v2.1.0 解锁版安装教程 (GPU三维地形生成软件)

前言 World Creator是一款功能相当强大的地形景观生成器&#xff1b;可以完全根据自己的需求来对地形、景观生成您需要三维模型&#xff0c;内置的大量预设&#xff0c;让您的创建拥有无限的可能性。 一、下载地址 下载链接&#xff1a;http://dygod/ITSource 点击搜索&…

前后端部署笔记

windows版&#xff1a; 如果傻呗公司让用win电脑部署&#xff0c;类似于我们使用笔记本做局域网服务器&#xff0c;社内使用。 1.安装win版的nginx、mysql、node、jdk等 2.nginx开机自启参考Nginx配置及开机自启动&#xff08;Windows环境&#xff09;_nginx开机自启动 wind…

学习100个Unity Shader (18) --- 几何着色器(Geometry Shader)

文章目录 概述编写格式举例应用举例&#xff08;用预制体球的每个顶点画一个立方体&#xff09;参考 概述 vertex shader --> [geometry shader] --> fragment shader。[]: 可选阶段。输入图元 —> geometry shader —> 其他图元 编写格式 [maxcertexcount(N)] …

源码编译安装LAMP与部署

目录 一、LAMP架构的简述 1.LAMP搭建时各组件安装顺序 二、编译安装Apache httpd服务 1.关闭防火墙&#xff0c;将安装Apache所需软件包传到/opt目录下 2.安装环境依赖包​编辑 3.配置软件模块 4.编译及安装 5.优化配置文件路径&#xff0c;并把httpd服务的可执行程序文…

2024斯佩菈音乐剧暑假训练营 30天创造奇迹培育坚强自信好少年

以声问本&#xff0c;素质教育&#xff0c;融会中西&#xff0c;知行合一。音乐是生活的灵魂&#xff0c;艺术的真正意义在于使人幸福&#xff0c;得到鼓舞和力量。多一把衡量的尺子&#xff0c;就会多出一批好学生。 相对学校语数英等学科教育&#xff0c;孩子心灵成长方面很…