首页 > 编程语言 >Java中的策略模式

Java中的策略模式

时间:2024-10-07 18:33:05浏览次数:1  
标签:Java 策略 模式 算法 AbstractStrategy context public

Java中的策略模式

综述

本文总结了策略模式的定义,特点,使用场景以及实现思路。

策略模式的定义

策略模式说通了, 就是定义一系列的算法, 将它们各自封装起来, 并且使用一个共同的接口使它们可相互替换. 使得算法和算法之间没有耦合, 这样如果方法需要修改或者添加, 工程师不需要修改那些无关的算法. 特别是当业务逻辑需要从多种算法之中挑选自己需要的算法时, 采用策略模式会非常的有效.

策略模式的特点和使用场合

策略模式主要使用场景在于将业务代码和具体算法进行解耦. 当某个业务代码需要用到某种算法来实现这个功能, 且不能保证未来一直永这类算法, 并共用同一个接口, 这时候就可以考虑策略模式. 常见的使用场景包括排序算法、缓存策略、支付方式等等

策略模式有以下优点:

  • 对算法或行为进行封装, 方便开发和理解.
  • 切换方便, 扩展也方便.

当然策略模式也有缺点, 策略模式为了将算法和业务模式解耦, 额外添加了类的数量以及代码量. 所以在算法数量较少的时候(特别是干脆只有一种算法的时候)不建议采用策略模式, 避免出现杀鸡用牛刀空耗时间与精力.

策略模式的实现思路

策略模式包含以下几个角色:

  • 上下文(Context):维护一个对策略对象的引用,负责将客户端请求委派给具体的策略对象执行。上下文类可以通过依赖注入、简单工厂等方式来获取具体策略对象。
  • 抽象策略(Abstract Strategy):定义了策略对象的公共接口或抽象类,规定了具体策略类必须实现的方法。
  • 具体策略(Concrete Strategy):实现了抽象策略定义的接口或抽象类,包含了具体的算法实现。

如果读者熟悉状态模式的话就会发现二者非常的相似, 因为状态模式的核心角色分别是 上下文 抽象状态和具体状态. 可以发现这两个模式在很多地方有类似之处, 这也是为什么很多开发者会选择用策略模式替代状态模式的原因. 但是两者仍然存在一些本质上的不同.
对于状态模式而言, 状态的切换应该是固定的, 切换顺序根据不同的实现思路应该内置于具体状态或者上下文之中. 而策略模式中不同的状态切换是外置的, 是可以交由context的实例的调用者根据需要以任意切换的.

策略模式示例

下面展示一个有足够泛用性示例, 策略模式同时还结合了工厂模式, 实际使用的时候还可以结合单例模式但是此处就不再继续拓展了

首先是 AbstractStrategy 接口, 这里内部的方法叫什么不重要, 重要的是所有策略都需要实现同样的方法, 既所有的策略必须接收同样的参数, 或者不接收任何参数, 当然如果不同的策略实际用到的参数不一样, 可以使用最大集构建一个接收所有参数的execute(para...) 方法, 这样用到哪个参数就调用哪个参数, 用不到就忽略

public interface AbstractStrategy {
    void execute();
}

然后是 AbstractStrategy 的各种实现类, 既单独的策略

public class FooBarConcreteStrategy implements AbstractStrategy{

    @Override
    public void execute() {
        //在真实的使用时可以在方法中进行任意操作, 
        //此处只使用打印文字作为举例, 下同
        System.out.println("foobar");
    }
}

public class HelloWorldConcreteStrategy implements AbstractStrategy{

    @Override
    public void execute() {
        System.out.println("hello, world");
    }
}

然后是context类

public class ExecuteContext {
    private AbstractStrategy strategy;

    // 设置策略
    public void setStrategy(AbstractStrategy strategy) {
        this.strategy = strategy;
    }

    // 执行策略
    public void executeStrategy() {
        strategy.execute();
        return;
    }
}

最后是一个调用的示例, 实际使用的时候当然要放在具体的方法中

public class StrategyPattern {
    public static void main(String[] args){
        ExecuteContext context = new ExecuteContext();
        context.setStrategy(new HelloWorldConcreteStrategy());
        context.executeStrategy();
    }
}

策略模式拓展版本

因为纯策略模式要求调用者熟悉当前需要测策略, 在易用性方面不够友好, 所以这里给出一个策略模式+简单工厂模式的拓展版本

相较于原来的策略模式, 修改context, 这里context同时还承担了factory的职责, 所以核心是STRATEGY_MAP, 用于对外返回需要的策略实例. 同时内置一个public类型的内部枚举类, 这样外部调用策略实例的时候可以通过枚举类的提示知道当前提供哪几种策略. 具体如下

public class ExecuteContextExtension {

    private static final Map<ExecuteStrategyName, AbstractStrategy> STRATEGY_MAP = new HashMap<>();

    static {
        STRATEGY_MAP.put(ExecuteStrategyName.HelloWorld, new HelloWorldConcreteStrategy());
        STRATEGY_MAP.put(ExecuteStrategyName.FooBar, new FooBarConcreteStrategy());
    }

    public AbstractStrategy getStrategy(ExecuteStrategyName name){
        return STRATEGY_MAP.get(name);
    }

    public enum ExecuteStrategyName {
        HelloWorld,
        FooBar,
    }
}

调用的写法为

public class StrategyPattern {
    public static void main(String[] args){
        ExecuteContextExtension factory = new ExecuteContextExtension();
        AbstractStrategy foobarStrategy = factory.getStrategy(ExecuteStrategyName.FooBar);

        foobarStrategy.execute();
}

其余不变
这一拓展的好处在于, 对于调用者而言它可以直接从context内置的枚举类中知道整个模块提供了哪几种策略. 易用性拉满, 当然缺点是对开闭原则的支持没有纯策略模式那么好.

总结

策略模式是23种设计模式中的一种, 属于行为模式这一细分类别, 主要用于解耦业务逻辑和具体算法. 个人以为, 策略模式属于和观察者模式以及工厂模式同一级别的标杆级设计模式, 构思精妙, 威力强大, 应用广泛. 当然和单例模式这种简单易行的模式不同, 也就没有速成之法, 新手从接触到上手总还是要半天时间.

标签:Java,策略,模式,算法,AbstractStrategy,context,public
From: https://www.cnblogs.com/dwcg/p/18175381

相关文章

  • Java基础第八章(多态)
    多态1.方法体现多态方法重载体现多态对象通过传入不同数量的参数,会调用不同的sun方法,体现出多态方法重写体现多态A类和B类是继承关系,通过不同对象调用对应类中重写的方法体现2.对象体现多态编译是javac,运行是java(1)一个对象的编译类型和运行类型可以不一致将父......
  • JavaScript 小知识:轻松搞定 ArrayBuffer 到 Base64 的转换
    关键词:ArrayBuffer,Base64,栈溢出,TextDecoder,btoa,性能优化,JavaScript,兼容性摘要本文探讨了在JavaScript中将ArrayBuffer转换为Base64字符串时遇到的栈溢出问题,并提供了几种实用的解决方案。我们将通过生动的比喻来解释相关概念,比较不同方法的性能和兼......
  • JavaScript 小知识:轻松搞定 ArrayBuffer 到 Base64 的转换
    关键词:ArrayBuffer,Base64,栈溢出,TextDecoder,btoa,性能优化,JavaScript,兼容性摘要本文探讨了在JavaScript中将ArrayBuffer转换为Base64字符串时遇到的栈溢出问题,并提供了几种实用的解决方案。我们将通过生动的比喻来解释相关概念,比较不同方法的性能和兼......
  • JavaScript 小知识:轻松搞定 ArrayBuffer 到 Base64 的转换
    关键词:ArrayBuffer,Base64,栈溢出,TextDecoder,btoa,性能优化,JavaScript,兼容性摘要本文探讨了在JavaScript中将ArrayBuffer转换为Base64字符串时遇到的栈溢出问题,并提供了几种实用的解决方案。我们将通过生动的比喻来解释相关概念,比较不同方法的性能和兼......
  • 基于JAVA的鲜花商城管理系统(源码+定制+讲解)鲜花商城管理系统、鲜花商城管理平台、鲜
    博主介绍:  ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W+粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台的优质作者。通过长期分享和实战指导,我致力于帮助更多学生......
  • Java与线程
    Java与线程1.线程的实现线程是比进程更轻量级的调度执行单位,线程的引人,可以把一个进程的资源分配和执行调度分开,各个线程既可以共享进程资源(内存地址、文件IO等),又可以独立调度。目前线程是Java里面进行处理器资源调度的最基本单位。主流的操作系统都提供了线程实现,Java语言......
  • java_day9_包的划分、形式参数、返回值、修饰符、内部类、匿名内部类
    一、包包的划分好处:1、方便管理我们的代码文件2、不同包下的文件名可以是一样的常见的包命名规则:1、按照功能划分增加-学生增加-老师增加删除-学生删除-老师删除修改......
  • 基于java+springboot的医院预约挂号系统小程序(源码+lw+部署文档+讲解等)
    课题简介医院预约挂号系统基于Java和SpringBoot开发,是改善医疗服务流程、提高患者就医体验的重要工具。该系统利用Java的稳定性和强大性能,以及SpringBoot的便捷开发框架,确保系统可靠运行和易于维护。它包含了患者信息管理、医生信息管理、科室信息管理、预约管......
  • 基于java+springboot的医学电子技术线上翻转课堂系统(源码+lw+部署文档+讲解等)
    课题简介医学电子技术线上翻转课堂系统基于Java和SpringBoot开发,是为医学教育领域量身打造的创新教学平台。该系统借助Java的稳定性和强大性能,以及SpringBoot的高效开发特性,确保系统能够稳定运行且易于维护和扩展。它涵盖了丰富的功能模块,包括课程资源管理、学......
  • 基于java+springboot的医疗设备管理系统(源码+lw+部署文档+讲解等)
    课题简介医疗设备管理系统基于Java和SpringBoot开发,是专为医疗机构设计的全面高效的设备管理解决方案。该系统利用Java的稳定性和强大性能,以及SpringBoot的便捷开发框架,确保系统可靠运行和易于维护。它涵盖了设备信息管理、采购管理、库存管理、维修管理、报废......