Java设计模式之策略模式详细讲解和案例示范

news2024/9/21 21:14:41

Java设计模式之策略模式详细讲解和案例示范

在软件开发中,策略模式是一种常见且非常有用的设计模式。它允许定义一系列算法,将它们一个个封装起来,并且使它们可以互相替换。策略模式让算法可以独立于使用它们的客户端而变化。本篇文章将详细介绍策略模式,包含它的应用场景、常见问题及解决方式,并会通过以电商交易系统为例子来说明。最后,我们还将对策略模式与工厂模式、适配器模式进行比较,探讨它们之间的区别。此外,还会介绍策略模式在开源框架中的实际应用。

一、策略模式的基本概念

策略模式(Strategy Pattern)定义了一系列算法,并将每个算法封装起来,使得它们可以互相替换。策略模式使得算法可以独立于使用它们的客户端而变化。策略模式的主要角色包括:

  1. Context(上下文):维护对某个策略对象的引用,用于客户端调用。
  2. Strategy(策略接口):定义策略方法,策略类都需要实现这个接口。
  3. ConcreteStrategy(具体策略类):实现策略接口,提供具体的算法实现。
    在这里插入图片描述
二、策略模式的使用场景

策略模式特别适用于以下场景:

  • 需要在多个算法中进行选择时:例如电商平台上的优惠券计算,有不同的计算方式,如满减、打折、返现等,可以使用策略模式将这些计算方式封装起来。
  • 算法的实现可以独立于使用它的客户类:例如,在订单结算过程中,可以根据不同的用户类型(会员、普通用户)选择不同的结算策略。
  • 算法需要在运行时根据不同条件动态切换:例如,电商系统中可以根据商品的种类选择不同的库存计算方式。
三、策略模式的电商交易系统示例

我们以电商交易系统中的“订单优惠计算”为例,来说明策略模式的应用。

  1. 定义策略接口
public interface DiscountStrategy {
    double calculateDiscount(Order order);
}
  1. 具体策略实现类
public class PercentageDiscountStrategy implements DiscountStrategy {
    @Override
    public double calculateDiscount(Order order) {
        // 10%折扣
        return order.getTotalAmount() * 0.10;
    }
}

public class FixedAmountDiscountStrategy implements DiscountStrategy {
    @Override
    public double calculateDiscount(Order order) {
        // 固定减免50元
        return 50.0;
    }
}
  1. 上下文类
public class Order {
    private DiscountStrategy discountStrategy;

    public Order(DiscountStrategy discountStrategy) {
        this.discountStrategy = discountStrategy;
    }

    public double getTotalAmount() {
        // 假设总金额为1000元
        return 1000.0;
    }

    public double calculateDiscount() {
        return discountStrategy.calculateDiscount(this);
    }
}
  1. 客户端使用
public class ECommercePlatform {
    public static void main(String[] args) {
        Order order = new Order(new PercentageDiscountStrategy());
        double discount = order.calculateDiscount();
        System.out.println("折扣金额: " + discount);

        order = new Order(new FixedAmountDiscountStrategy());
        discount = order.calculateDiscount();
        System.out.println("折扣金额: " + discount);
    }
}
四、常见问题及解决方式(附代码示例)
  1. 策略类过多的问题:在策略模式中,每个算法都需要一个具体的策略类,当策略较多时,可能会导致类的数量急剧增加。可以通过使用匿名内部类或Lambda表达式来简化代码,避免类爆炸。

    问题示例

    public class SeasonalDiscountStrategy implements DiscountStrategy {
        @Override
        public double calculateDiscount(Order order) {
            return order.getTotalAmount() * 0.15;
        }
    }
    
    public class NewCustomerDiscountStrategy implements DiscountStrategy {
        @Override
        public double calculateDiscount(Order order) {
            return order.getTotalAmount() * 0.20;
        }
    }
    

    解决方式

    通过使用匿名内部类或Lambda表达式,减少具体策略类的数量:

    public class ECommercePlatform {
        public static void main(String[] args) {
            Order order = new Order(o -> o.getTotalAmount() * 0.15);
            double discount = order.calculateDiscount();
            System.out.println("季节折扣金额: " + discount);
    
            order = new Order(o -> o.getTotalAmount() * 0.20);
            discount = order.calculateDiscount();
            System.out.println("新客户折扣金额: " + discount);
        }
    }
    
  2. Context类依赖具体策略类的问题:上下文类需要与具体的策略类耦合,可能会导致代码的灵活性降低。可以通过工厂模式来动态选择和生成具体策略类。

    问题示例

    public class Order {
        private DiscountStrategy discountStrategy;
    
        public Order(DiscountStrategy discountStrategy) {
            this.discountStrategy = discountStrategy;
        }
    
        public double calculateDiscount() {
            return discountStrategy.calculateDiscount(this);
        }
    }
    

    解决方式

    使用工厂模式来动态生成策略实例:

    public class DiscountStrategyFactory {
        public static DiscountStrategy getStrategy(String strategyType) {
            if ("Percentage".equals(strategyType)) {
                return o -> o.getTotalAmount() * 0.10;
            } else if ("FixedAmount".equals(strategyType)) {
                return o -> 50.0;
            } else {
                throw new IllegalArgumentException("Unknown strategy type");
            }
        }
    }
    
    public class ECommercePlatform {
        public static void main(String[] args) {
            Order order = new Order(DiscountStrategyFactory.getStrategy("Percentage"));
            double discount = order.calculateDiscount();
            System.out.println("折扣金额: " + discount);
        }
    }
    
五、策略模式与工厂模式的区别

策略模式与工厂模式在某些场景中可以配合使用,但它们的目的和应用场景有所不同:

  • 策略模式:侧重于算法的替换,它封装了不同的算法或行为,使它们在不同的场景下可以互换。
  • 工厂模式:则用于创建对象,它解决了对象的实例化问题。

在策略模式中,算法已经存在,策略模式只是选择哪一个算法;而在工厂模式中,我们关注的是如何创建这些算法对应的对象。

六、策略模式与适配器模式的区别

策略模式与适配器模式在结构上有一定的相似性,但它们的意图完全不同:

  • 策略模式:用于替换算法,即可以在运行时替换算法的实现。
  • 适配器模式:用于将一个接口转化为另一个接口,它主要是为了兼容现有系统或库。
七、在开源框架中的应用示范

在Spring中,策略模式常用于任务调度、视图解析、事务管理等模块。我们以Resource接口的实现为例来说明策略模式的应用。

Resource接口

在Spring中,Resource接口用于统一资源访问。Spring提供了多种Resource实现,如UrlResourceClassPathResource等。开发者可以根据需要选择不同的资源访问策略。

public interface Resource {
    InputStream getInputStream() throws IOException;
    // 其他方法省略...
}

public class UrlResource implements Resource {
    private final URL url;

    public UrlResource(URL url) {
        this.url = url;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        return url.openStream();
    }
}

public class ClassPathResource implements Resource {
    private final String path;

    public ClassPathResource(String path) {
        this.path = path;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        return getClass().getClassLoader().getResourceAsStream(path);
    }
}

如何使用策略模式

在应用中,你可以选择合适的Resource实现来加载资源。例如,当你需要加载外部URL资源时,可以使用UrlResource;而加载类路径资源时,可以使用ClassPathResource

public class ResourceLoader {

    public void loadResource(Resource resource) {
        try (InputStream is = resource.getInputStream()) {
            // 处理输入流
            System.out.println("Resource Loaded: " + resource.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        ResourceLoader loader = new ResourceLoader();
        
        // 使用URL策略
        Resource urlResource = new UrlResource(new URL("http://example.com/data.txt"));
        loader.loadResource(urlResource);

        // 使用类路径策略
        Resource classPathResource = new ClassPathResource("data.txt");
        loader.loadResource(classPathResource);
    }
}
八、总结

策略模式是一个非常强大的设计模式,它使得算法可以在不同的上下文中灵活地替换。在电商交易系统中,策略模式可以用于实现各种复杂的业务逻辑,如优惠计算、库存管理等。通过策略模式,我们可以将不同的算法进行封装,并在运行时动态选择,从而使代码更加灵活、易于维护。同时,我们还探讨了策略模式与工厂模式、适配器模式的区别,并展示了在Spring框架中的实际应用。

通过这些内容,相信你对策略模式有了更加深入的理解,并能够在实际项目中灵活运用这一设计模式。如果你在项目中遇到类似的问题,可以尝试使用策略模式来解决,可能会有意想不到的效果。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VScode的python虚拟环境

1 创建虚拟环境(venv) 在VSCode中打开项目文件夹,键盘按住快捷键ctrl shift p,打开命令面板,输入python:创建环境 选择venv,输入解释器路径,此时左侧文件夹内会出现一个.venv文件夹 2 激活虚拟…

未来已来:探索机器学习如何重塑人工智能的未来方向

引言:机器学习室实现人工智能的关键技术手段,应用领域持续延伸 机器学习是人工智能的一个重要分支,主要研究如何让计算机系统通过数据学习并做出决策或预测,而不需要明确的编程。简单来说,就是让计算机利用经验来提高性…

C# 如何实现接口事件:详解与示例

文章目录 实现接口事件的步骤示例:实现接口事件1. 定义接口事件2. 实现接口事件3. 订阅和触发事件4. 使用示例 总结 在C#中,接口(interface)是一种定义类必须实现的方法和属性的抽象类型。除了方法和属性,接口还可以包…

浅谈红队攻防之道-CobaltStrike钓鱼攻击集锦

打个比方,一片大地上,躺着一群沉睡的人,远处就是火山,马上就要爆发了,你就像个闹钟,面对这些沉睡的人,你想把他们叫醒。 你持续不断地响着,有的睡得浅的人,被你叫醒了&am…

区块链基础通识(1)——分布式系统的共识问题

分布式系统 我们最初了解区块链的时候,很多人会形容这个区块链是一个“分布式的不可篡改账本”,这是一个很形象的说法,但是我认为更为准确的形容是“所有节点共同维护的状态机”。为什么分布式和区块链不能划等号呢? 两种常见的…

Ubuntu 22.04中解决Could not load the Qt platform plugin “xcb“问题解决方法

摘要:在Ubuntu 22.04中安装OpenCV后,遇到“load the Qt platform plugin “xcb” in site-packages/cv2/qt/plugins" even though it was found. 的问题,导致程序无法启动。本文详细探讨了该问题的成因,并介绍了几种常见但无…

在线英语学习小程序App源码开发技术探讨

引言 随着信息技术的飞速发展和全球化进程的加快,英语学习已经成为越来越多人的日常需求。传统的纸质材料和课堂教学已经无法满足现代人灵活、高效的学习需求。因此,开发一款在线英语学习小程序App成为了一个热门话题。本文将从技术角度探讨在线英语学习…

SX_gitlab图形化案例_19

由图形去理解gitlab反而更直观: 圆圈代表着本机代码所在的位置 这就代表着,本机的代码和远程仓库,jhy_gnss的代码是一样的 一个原点代表着一次改动 merge branch ‘jhy_gnss’ of 192.168.91.10:t3000 into jhy_gnss 这条命令是将GitLab服…

Frog4Shell — FritzFrog 僵尸网络将一日攻击纳入其武器库

FritzFrog 的背景 Akamai 通过我们的全球传感器网络持续监控威胁,包括我们之前发现的威胁。其中包括FritzFrog 僵尸网络(最初于 2020 年发现),这是一个基于 Golang 的复杂点对点僵尸网络,经过编译可同时支持基于 AMD 和 ARM 的机器。该恶意软件得到积极维护,多年来通过增…

基于FPGA的ASIC prototype验证

在当今快速发展的电子设计自动化(EDA)领域,专用集成电路(ASIC)的开发因其高性能、低功耗和定制化的特点而备受青睐。然而,ASIC的设计和制造过程不仅成本高昂,而且周期漫长,一旦进入生…

数学建模之数据分析【八】:数据预处理之数据格式化

文章目录 一、在Pandas中格式化数据框的浮点列1.1 将列值四舍五入到两位小数1.2 使用逗号和小数精度的 Pandas DataFrame 格式1.3 在 Pandas DataFrame 中格式化和缩放人口数据 二、如何检查Pandas DataFrame 中的数据类型2.1 创建 DataFrame 检查 DataType2.1.1 创建数据集2.1…

《前端攻城狮 · Vue 使用腾讯地图》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗 🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数…

React18快速入门教程

项目流程 开发流程 技术选型 创建项目 执行命令: pnpm create vite项目配置 editorconfig:用于配置编辑器,实现使用不同的编辑器打开效果是相同的包配置:主要用于配置国内源eslint配置:主要用于配置语法规则prettier&…

leetCode - - - 二分查找

目录 1.二分查找(Leetcode 704) 2.搜索插入位置( LeetCode 35 ) 3.寻找峰值(LeetCode 162) 4.旋转数组的最小数字(BM21) 5.总结 1.二分查找(Leetcode 704&#xff0…

基于Python的机器学习系列(7):多元逻辑回归

在本篇博文中,我们将探讨多元逻辑回归,它是一种扩展的逻辑回归方法,适用于分类数量超过两个的场景。与二元逻辑回归不同,多元逻辑回归使用Softmax函数将多个类别的概率输出映射到[0, 1]范围内,并确保所有类别的概率和为…

利用漏洞实现 Outlook 的 RCE:第一部分

概述 2023 年 3月补丁星期二解决的漏洞中,有一个是Outlook 的一个严重漏洞,编号为CVE-2023-23397,该漏洞被 Forest Blizzard 在野利用,微软已将其确定为俄罗斯国家支持的威胁行为者。2023 年 12 月,微软与波兰网络司令部 (DKWOC) 联合发布消息称,他们发现同一威胁行为者…

Debug-023-Document.createElement()的使用

Document.createElement() document.createElement()是在对象中创建一个对象,要与appendChild() 或 insertBefore()方法联合使用。 appendChild() 方法在节点的子节点列表末添加新的子节点。 insertBefore() 方法在节点的子节点列表任意位置插入新的节点。 用途举…

Linux -- git

1 啥是git git是一个代码的历史版本管理工具,通过用树形结构管理一个代码版本可以快速实现回滚等操作 1.1 git基本概念 工作区(Working Directory/Working Tree): 这是你当前正在处理项目文件的地方。你可以在工作区中创建、修改…

非关系型数据库MongoDB(文档型数据库)介绍与使用实例

MongoDB介绍 MongoDB是一种开源的文档型数据库管理系统,它使用类似于JSON的BSON格式(Binary JSON)来存储数据。与传统关系型数据库不同,MongoDB不使用表和行的结构,而是采用集合(Collection)(My…

漏洞发现——漏洞扫描工具的对比

本帖字的实验环境是来自学校的靶机 文章目录 Xray介绍安装教程使用教程主动扫描单个url扫描批量扫描 被动扫描联合游览器联合burpsuite Awvs介绍安装教程使用教程联合xary三者联合bp和xray Goby介绍安装教程使用教程 Afrog介绍安装教程使用教程 Vulmap介绍安装教程使用教程 Poc…