使用 Function 来编写策略模式:优雅而高效的设计模式实践

引言:为什么选择策略模式?

策略模式(Strategy Pattern)是行为设计模式中的经典之一,它允许我们定义一系列的算法或操作,并使得它们可以互换使用。策略模式的关键思想是将算法的实现与使用它们的上下文分离,使得同一操作可以根据不同的策略来实现。

但在 Java 中,如何实现这个设计模式呢?通常我们会使用接口、抽象类和具体实现来完成,但这往往导致代码复杂、冗长。那么,如何利用 Java 8 引入的 Function 来使策略模式变得更加简洁和优雅呢?

这篇文章将展示如何通过 Function 来实现策略模式,从而使得策略模式更加灵活、简洁且易于维护。


一、策略模式的传统实现

首先,让我们看看传统的策略模式是如何实现的。在这个例子中,我们有一个 PaymentStrategy,它有多种不同的支付方式,例如 信用卡支付PayPal 支付

1. 定义策略接口:
interface PaymentStrategy {void pay(double amount);
}
2. 实现具体策略:
class CreditCardPayment implements PaymentStrategy {private String cardNumber;public CreditCardPayment(String cardNumber) {this.cardNumber = cardNumber;}@Overridepublic void pay(double amount) {System.out.println("Paid " + amount + " using Credit Card: " + cardNumber);}
}class PayPalPayment implements PaymentStrategy {private String email;public PayPalPayment(String email) {this.email = email;}@Overridepublic void pay(double amount) {System.out.println("Paid " + amount + " using PayPal account: " + email);}
}
3. 策略上下文:
class PaymentContext {private PaymentStrategy strategy;public PaymentContext(PaymentStrategy strategy) {this.strategy = strategy;}public void executePayment(double amount) {strategy.pay(amount);}
}
4. 客户端代码:
public class Main {public static void main(String[] args) {PaymentStrategy creditCardPayment = new CreditCardPayment("1234-5678-9876");PaymentStrategy payPalPayment = new PayPalPayment("user@example.com");PaymentContext context = new PaymentContext(creditCardPayment);context.executePayment(100.00);context = new PaymentContext(payPalPayment);context.executePayment(200.00);}
}

通过这种方式,我们使用不同的策略来支付不同的金额。代码看起来清晰,但我们有很多重复的代码结构,如 PaymentStrategy 接口和各个策略类的实现。接下来,我们将使用 Function 来改进这一设计。


二、使用 Function 改进策略模式

在 Java 8 引入的 Function 可以帮助我们简化这个过程。Function 是一个函数式接口,它接受一个输入并返回一个结果。在策略模式中,我们将 Function 作为策略的实现来代替原本的类结构。

1. 使用 Function 定义策略:

我们不再需要定义一个接口或多个类来实现不同的策略,而是直接使用 Function 来表示每种策略。每个 Function 接收一个 double 类型的支付金额并执行支付操作。

2. 改进的策略代码:
import java.util.function.Function;public class PaymentStrategy {// 使用 Function 来表示支付策略public static Function<Double, Void> creditCardPayment(String cardNumber) {return amount -> {System.out.println("Paid " + amount + " using Credit Card: " + cardNumber);return null; // 返回类型为 Void,表示没有返回值};}public static Function<Double, Void> payPalPayment(String email) {return amount -> {System.out.println("Paid " + amount + " using PayPal account: " + email);return null;};}
}
3. 策略上下文(更简化):
class PaymentContext {private Function<Double, Void> strategy;public PaymentContext(Function<Double, Void> strategy) {this.strategy = strategy;}public void executePayment(double amount) {strategy.apply(amount);  // 使用 Function 的 apply 方法执行支付}
}
4. 客户端代码:
public class Main {public static void main(String[] args) {// 使用 Function 传递支付策略Function<Double, Void> creditCardPayment = PaymentStrategy.creditCardPayment("1234-5678-9876");Function<Double, Void> payPalPayment = PaymentStrategy.payPalPayment("user@example.com");PaymentContext context = new PaymentContext(creditCardPayment);context.executePayment(100.00);context = new PaymentContext(payPalPayment);context.executePayment(200.00);}
}

三、优势分析:

  1. 简洁的代码:

    • 使用 Function 来代替传统的接口和具体类,不仅减少了类的数量,而且让代码更加简洁。我们不需要为每个策略创建一个类,所有策略的实现都可以在一个地方集中定义。
  2. 灵活性:

    • Function 可以非常容易地通过 Lambda 表达式来定义,也可以根据需求动态调整策略。而且,Function 是一个高度可组合的接口,可以通过链式调用来组合多个函数。
  3. 代码维护:

    • 使用 Function 来表达策略使得每个策略变得更加简洁且独立,开发者可以轻松地替换策略或修改策略的行为,而无需修改复杂的类结构。
  4. 与 Java 8+ 特性结合:

    • 结合 Java 8 的 Lambda 表达式、Stream API 等特性,Function 让代码更加符合现代 Java 编程风格。

四、进一步优化:策略的复用和组合

一个重要的应用场景是我们可以通过组合多个 Function 来复用现有策略或创建新策略。例如,我们可以组合支付策略和折扣策略来构建更复杂的支付流程。

示例:组合策略

假设我们需要为支付金额应用折扣:

public class DiscountedPayment {public static Function<Double, Double> applyDiscount(double discountRate) {return amount -> amount - (amount * discountRate);}public static Function<Double, Void> creditCardPaymentWithDiscount(String cardNumber, double discountRate) {Function<Double, Double> discount = applyDiscount(discountRate);return amount -> {double discountedAmount = discount.apply(amount);System.out.println("Paid " + discountedAmount + " using Credit Card: " + cardNumber);return null;};}
}

我们可以将折扣策略与支付策略组合:

Function<Double, Void> creditCardPaymentWithDiscount = DiscountedPayment.creditCardPaymentWithDiscount("1234-5678-9876", 0.1);
PaymentContext context = new PaymentContext(creditCardPaymentWithDiscount);
context.executePayment(100.00);  // 输出:Paid 90.0 using Credit Card: 1234-5678-9876

五、总结:优雅而高效的策略模式

通过 Function,我们不仅让策略模式更加简洁,而且增强了代码的灵活性和可维护性。借助 Lambda 表达式,Java 8+ 中的函数式编程特性,我们能够以一种更现代、更优雅的方式实现策略模式。

本篇要点回顾:
  1. 使用 Function 替代传统的策略接口与具体实现类,简化了策略模式的实现。
  2. Function 的高度灵活性和组合能力使得策略模式更加可扩展。
  3. 结合 Java 8+ 的特性,策略模式变得更加优雅、高效,减少了代码冗余。

你可以尝试在自己的项目中应用这个技巧,提升代码的简洁性和可维护性。

推荐阅读文章

  • 由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)

  • 如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系

  • HTTP、HTTPS、Cookie 和 Session 之间的关系

  • 什么是 Cookie?简单介绍与使用方法

  • 什么是 Session?如何应用?

  • 使用 Spring 框架构建 MVC 应用程序:初学者教程

  • 有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误

  • 如何理解应用 Java 多线程与并发编程?

  • 把握Java泛型的艺术:协变、逆变与不可变性一网打尽

  • Java Spring 中常用的 @PostConstruct 注解使用总结

  • 如何理解线程安全这个概念?

  • 理解 Java 桥接方法

  • Spring 整合嵌入式 Tomcat 容器

  • Tomcat 如何加载 SpringMVC 组件

  • “在什么情况下类需要实现 Serializable,什么情况下又不需要(一)?”

  • “避免序列化灾难:掌握实现 Serializable 的真相!(二)”

  • 如何自定义一个自己的 Spring Boot Starter 组件(从入门到实践)

  • 解密 Redis:如何通过 IO 多路复用征服高并发挑战!

  • 线程 vs 虚拟线程:深入理解及区别

  • 深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别

  • 10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!

  • “打破重复代码的魔咒:使用 Function 接口在 Java 8 中实现优雅重构!”

  • Java 中消除 If-else 技巧总结

  • 线程池的核心参数配置(仅供参考)

  • 【人工智能】聊聊Transformer,深度学习的一股清流(13)

  • Java 枚举的几个常用技巧,你可以试着用用

  • 由 Spring 静态注入引发的一个线上T0级别事故(真的以后得避坑)

  • 如何理解 HTTP 是无状态的,以及它与 Cookie 和 Session 之间的联系

  • HTTP、HTTPS、Cookie 和 Session 之间的关系

  • 使用 Spring 框架构建 MVC 应用程序:初学者教程

  • 有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误

  • Java Spring 中常用的 @PostConstruct 注解使用总结

  • 线程 vs 虚拟线程:深入理解及区别

  • 深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别

  • 10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!

  • 探索 Lombok 的 @Builder 和 @SuperBuilder:避坑指南(一)

  • 为什么用了 @Builder 反而报错?深入理解 Lombok 的“暗坑”与解决方案(二)

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

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

相关文章

Windows 系统中安装 Git 并配置 GitHub 账户

由于电脑重装系统&#xff0c;重新配置了git. 以下是在 Windows 系统中安装 Git 并配置 GitHub 账户的详细步骤&#xff1a; 1. 安装 Git 访问 Git 官网下载页面下载 Windows 版本的 Git 安装程序运行安装程序&#xff0c;使用默认选项即可 2. 配置 Git 用户信息 打开命令…

MergeX亮相GTC2025:开启全球广告流量交易新篇章

全球流量盛宴GTC2025深圳启幕&#xff0c;共探出海新蓝海 2025年4月24日至25日&#xff0c;GTC2025全球流量大会将在深圳福田会展中心9号馆隆重召开。作为跨境出海领域内规模最大、资源最丰富、产业链最完备的年度盛会&#xff0c;此次大会将汇聚众多行业精英&#xff0c;共同探…

kubernetes》》k8s》》Volume 数据卷 PVC PV NFS

为啥需要数据卷 容器磁盘上的文件的生命周期是短暂的&#xff0c;这就使得在容器中运行重要应用时会出现一些问题。首先&#xff0c;当容器崩溃时&#xff0c;kubelet会重启它&#xff0c;但是容器中的文件将丢失——容器以干净的状态&#xff08;镜像最初的状态&#xff09;重…

第十六届蓝桥杯 省赛C/C++ 大学B组

编程题目现在在洛谷上都可以提交了。 未完待续&#xff0c;写不动了。 C11 编译命令 g A.cpp -o A -Wall -lm -stdc11A. 移动距离 本题总分&#xff1a;5 分 问题描述 小明初始在二维平面的原点&#xff0c;他想前往坐标 ( 233 , 666 ) (233, 666) (233,666)。在移动过程…

谷歌怎么设置在新标签页中打开网页

按图示操作即可&#xff0c;藏得真深啊&#xff0c;无语&#xff0c;而且就算打开了&#xff0c;点收藏夹&#xff0c;顶部快捷栏里的网站&#xff0c;网站里的连接&#xff0c;打开也还是覆盖原来的&#xff0c;呵呵呵呵呵呵呵&#xff0c;有没有人管管 另外我的edge不知咋滴…

【企业级数据安全】掌握高性能Log4j2敏感信息脱敏方案

前言 在数据安全合规日益严格的今天&#xff0c;日志中的敏感信息保护已成为企业IT建设的必备环节。本文带您深入了解如何打造一套高性能、可实时配置的Log4j2日志脱敏插件&#xff0c;轻松应对各类敏感数据保护需求&#xff0c;让您的系统既满足合规要求&#xff0c;又不牺牲…

Linux中的tar -P选项

tar -P选项 Linux中的tar命令可用于文件和目录的归档以及压缩解压缩。而其中的-P选项是什么含义呢&#xff1f;下面我们就来看一看 1、不添加-P选项 对于如下压缩命令&#xff1a; tar -czvf pkg.tar.gz /opt/software执行该命名&#xff0c;控制台首行输出将会提示&#xf…

【2025年泰迪杯数据挖掘挑战赛】B题 详细解题思路+数据预处理+代码分享

目录 2025年泰迪杯B题详细解题思路问题一问题分析数学模型Python代码Matlab代码 问题二问题分析数学模型Python代码Matlab代码 问题三问题分析数学模型Python代码Matlab代码 问题四问题分析数学模型Python代码Matlab代码 2025年泰迪杯B题详细解题思路 初步分析整理了B题的赛题分…

SpringBoot3快速入门笔记

springboot3简介 SpringBoot 帮我们简单、快速地创建一个独立的、生产级别的 Spring 应用&#xff08;说明&#xff1a;SpringBoot底层是Spring&#xff09; 大多数 SpringBoot 应用只需要编写少量配置即可快速整合 Spring 平台以及第三方技术 特性&#xff1a; ● 快速创建…

记录centos8安装宝塔过程(两个脚本)

1、切换系统源&#xff08;方便使用宝塔安装脚本下载&#xff09; bash <(curl -sSL https://linuxmirrors.cn/main.sh) 2、宝塔安装脚本在宝塔的官网 宝塔面板下载&#xff0c;免费全能的服务器运维软件 根据自己的系统选择相应的脚本 urlhttps://download.bt.cn/insta…

Xdocreport实现根据模板导出word

只使用freemaker生成简单的word文档很容易&#xff0c;但是当word文档需要插入动态图片&#xff0c;带循环数据&#xff0c;且含有富文本时解决起来相对比较复杂&#xff0c;但是使用Xdocreport可以轻易解决。 Xdocreport既可以实现文档填充也可以实现文档转换&#xff0c;此处…

VMware Fusion Pro/Player 在 macOS 上的完整安装与使用指南

VMware Fusion Pro/Player 在 macOS 上的完整安装与使用指南—目录 一、VMware 产品说明二、下载 VMware Fusion三、安装前准备四、安装 VMware Fusion步骤 1&#xff1a;安装程序步骤 2&#xff1a;首次启动配置步骤 3&#xff1a;输入许可证 五、创建虚拟机步骤 1&#xff1a…

Redis常用数据结构和应用场景

一、前言 Redis提供了多种数据结构&#xff0c;每种结构对应不同的应用场景。本文对部分常用的核心数据结构和典型使用场景作出介绍。 二、String&#xff08;字符串&#xff09; 特点&#xff1a;二进制安全&#xff0c;可存储文本、数字、序列化对象等。场景&#xff1a; 缓…

spring security oauth2.0的四种模式

OAuth 2.0 定义了 4 种授权模式&#xff08;Grant Type&#xff09;&#xff0c;用于不同场景下的令牌获取。以下是每种模式的详细说明、适用场景和对比&#xff1a; 一、授权码模式&#xff08;Authorization Code Grant&#xff09; 适用场景 • Web 应用&#xff08;有后端…

Oracle 排除交集数据 MINUS

MINUS 是 Oracle 数据库中的一种集合操作符&#xff0c;用于返回第一个查询结果中存在但第二个查询结果中 不存在 的 唯一行。其核心功能是 排除交集数据&#xff0c;常用于数据差异分析或过滤特定记录 一、核心功能 排除交集&#xff1a;返回第一个查询结果中 不在第二个查询结…

WiFi那些事儿(四)

目录 一、IEEE 802.11ah标准简介 二、IEEE 802.11ah信道特点 三、IEEE 802.11ah传输模式 在WiFi通信领域&#xff0c;信号绕射能力一直是一个关键问题。常规的WiFi设备多工作在2.4GHz和5GHz频段&#xff0c;这些频段的电磁波波长通常小于障碍物尺寸&#xff0c;受电磁波本身…

C++在Linux上生成动态库并调用接口测试

加减乘除demo代码 项目结构 CPP/ ├── calculator.cpp ├── calculator.h ├── main.cpp 头文件 #ifndef CALCULATOR_H #define CALCULATOR_H#ifdef __cplusplus extern "C" {#endifdouble add(double a, double b);double subtract(double a, double b…

离线密码生成器:安全可靠的密码管理解决方案

离线密码生成器&#xff1a;安全可靠的密码管理解决方案 在当今数字时代&#xff0c;我们每天都需要使用各种网站和应用程序&#xff0c;每个账户都需要一个强密码来保护我们的个人信息和隐私。然而&#xff0c;记住多个复杂的密码几乎是不可能的任务。今天&#xff0c;我要向…

ChatRex: Taming Multimodal LLM for Joint Perception and Understanding 论文理解和翻译

一、TL&#xff1b;DR MLLM在感知方面存在不足&#xff08;远远比不上专家模型&#xff09;&#xff0c;比如Qwen2-VL在coco上recall只有43.9%提出了ChatRex&#xff0c;旨在从模型设计和数据开发两个角度来填补这一感知能力的缺口ChatRex通过proposal边界框输入到LLM中将其转…

自动驾驶技术-相机_IMU时空标定

自动驾驶技术-相机_IMU时空标定 时间延迟 时间延迟 参考链接1、2 相机主要分为全局和卷帘快门相机&#xff0c;从触发到成像的过程包括&#xff1a;复位时间、AE()曝光时间、读出时间 全局快门如下图所示 卷帘快门如下图所示 相机录制视频时&#xff0c;为了保持固定频率&am…