使用 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.pswp.cn/diannao/77694.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/77694.shtml
英文地址,請注明出處:http://en.pswp.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…