設計模式探索:策略模式

1. 什么是策略模式(Strategy Pattern)

定義

策略模式(Strategy Pattern)的原始定義是:定義一系列算法,將每一個算法封裝起來,并使它們可以相互替換。策略模式讓算法可以獨立于使用它的客戶端而變化。

目的

策略模式的目的是在軟件開發中,當實現某一個功能存在多種算法或者策略時,可以根據環境或者條件的不同選擇不同的算法或者策略來完成該功能。
比如網購,你可以選擇工商銀行、農業銀行、建設銀行等等,但是它們提供的算法都是一致的,就是幫你付款。
在這里插入圖片描述

角色

策略模式的主要角色如下:

  1. 抽象策略(Strategy)類:這是一個抽象角色,通常由一個接口或抽象類實現。此角色給出所有的具體策略類所需的接口。
  2. 具體策略(Concrete Strategy)類:實現了抽象策略定義的接口,提供具體的算法實現或行為。
  3. 環境或上下文(Context)類:是使用算法的角色,持有一個策略類的引用,最終給客戶端調用。

UML類圖

在策略模式中可以定義一些獨立的類來封裝不同的算法,每一個類封裝一種具體的算法,在這里每一個封裝算法的類都可以被稱為一種策略,為了保證這些策略在使用時具有一致性,一般會提供一個抽象的策略類來做算法的聲明.而每種算法對應一個具體的策略類。
在這里插入圖片描述

實現代碼

// 抽象策略類
public interface Strategy {void algorithm();
}// 具體策略類A
public class ConcreteStrategyA implements Strategy {@Overridepublic void algorithm() {System.out.println("執行策略A");}
}// 具體策略類B
public class ConcreteStrategyB implements Strategy {@Overridepublic void algorithm() {System.out.println("執行策略B");}
}// 環境類
public class Context {// 維持一個對抽象策略類的引用private Strategy strategy;public Context(Strategy strategy) {this.strategy = strategy;}// 調用策略類中的算法public void algorithm() {strategy.algorithm();}
}// 客戶端代碼
public class Client {public static void main(String[] args) {Strategy strategyA = new ConcreteStrategyA();Context context = new Context(strategyA); // 可以在運行時指定類型context.algorithm();}
}

2.優缺點

優點

  1. 易于擴展和維護:由于不同的算法被封裝在不同的類中,所以我們可以很容易地添加新的算法或修改已有算法,而不需要修改客戶端的代碼。
  2. 提高代碼的可讀性:將不同的算法封裝在不同的類中,使得代碼更加模塊化,易于理解和維護。
  3. 消除大量的條件語句:使用策略模式,我們可以將不同的算法替換成不同的類,從而消除大量的if-else語句,使得代碼更加簡潔和易于理解。

缺點

  1. 需要額外的類和接口:使用策略模式,我們需要為每個算法都創建一個獨立的類,從而增加了代碼的復雜度。
  2. 客戶端需要知道所有的策略類:使用策略模式,客戶端需要知道所有的策略類,以便在運行時選擇合適的策略。這可能會增加代碼的復雜度。

應用場景

策略模式適用于以下場景:

  1. 需要根據不同的條件選擇不同的算法時:例如,計算器程序需要根據用戶輸入的運算符選擇相應的計算方法。
  2. 需要在運行時動態地選擇算法時:例如,某個系統需要根據用戶的配置或環境變量來選擇合適的算法。
  3. 需要將算法的實現細節與客戶端代碼分離時:例如,某個系統需要根據不同的數據來源來解析數據,但是客戶端并不關心數據的解析細節。

總結

策略模式通過定義一系列算法,將每一個算法封裝起來,并使它們可以相互替換,從而讓算法可以獨立于使用它的客戶端而變化。通過使用策略模式,可以提高代碼的擴展性、可讀性和維護性,同時也可以消除大量的條件語句。

在工作中,為了消除代碼中的大量 if-else 語句并提升代碼的可維護性和擴展性,可以使用策略模式。下面是詳細的實現步驟和代碼示例。

3.如何用設計模式消除代碼中的ifelse(你在工作中使用過哪些設計模式)

不使用設計模式

這是一個請假審批流程的代碼示例,包含員工類、請假單類和審核類,直接使用 if-else 語句來處理不同的審批規則。

public class Employee {private String name; // 姓名private int level;   // 級別: P6, P7, P8// Constructor, getters and setters
}public class LeaveForm {private Employee employee; // 員工private String reason;     // 請假原因private int days;          // 天數private int type;          // 類型: 0-病假, 1-婚喪假, 2-年假// Constructor, getters and setters
}public class LeaveService {public void audit(LeaveForm leaveForm) {// 3天以下婚喪假, 自動通過if (leaveForm.getDays() <= 3 && leaveForm.getType() == 1) {System.out.println("三天以下婚喪假 無需審批自動通過!");}// 3天以上婚喪假else if (leaveForm.getDays() > 3 && leaveForm.getType() == 1) {System.out.println("三天以上婚喪假 進入上級審批流程!");}// 總經理請假else if (leaveForm.getEmployee().getLevel() == 9) {System.out.println("總經理請假無需審批自動通過!");}// 一天病假else if (leaveForm.getDays() == 1 && leaveForm.getType() == 0) {System.out.println("一天病假無需審批自動通過!");}// 一天以上病假else if (leaveForm.getDays() > 1 && leaveForm.getType() == 0) {System.out.println("一天以上病假進入審批流程!");}}
}

使用策略模式進行優化

通過策略模式,將所有的 if-else 分支的業務邏輯抽取為各種策略類,判斷條件和執行邏輯封裝到對應的策略類中,讓客戶端去依賴策略接口,保證具體策略類的改變不影響客戶端。

策略接口
public interface AuditStrategy {boolean isSupport(LeaveForm leaveForm);void audit(LeaveForm leaveForm);int getPriority();String getName();
}
具體策略類
public class AuditStrategyImpl_1 implements AuditStrategy {@Overridepublic boolean isSupport(LeaveForm leaveForm) {return leaveForm.getDays() <= 3 && leaveForm.getType() == 1;}@Overridepublic void audit(LeaveForm leaveForm) {System.out.println(leaveForm);System.out.println("三天以下婚喪假 無需審批自動通過!");}@Overridepublic int getPriority() {return 0;}@Overridepublic String getName() {return "三天以下婚假審批規則";}
}public class AuditStrategyImpl_2 implements AuditStrategy {@Overridepublic boolean isSupport(LeaveForm leaveForm) {return leaveForm.getDays() > 3 && leaveForm.getType() == 1;}@Overridepublic void audit(LeaveForm leaveForm) {System.out.println(leaveForm);System.out.println("三天以上婚喪假 進入上級審批流程!");}@Overridepublic int getPriority() {return 0;}@Overridepublic String getName() {return "三天以上婚喪假審批規則";}
}public class AuditStrategyImpl_3 implements AuditStrategy {@Overridepublic boolean isSupport(LeaveForm leaveForm) {return leaveForm.getEmployee().getLevel() == 9;}@Overridepublic void audit(LeaveForm leaveForm) {System.out.println(leaveForm);System.out.println("總經理請假無需審批自動通過!");}@Overridepublic int getPriority() {return 999;}@Overridepublic String getName() {return "總經理請假審批規則";}
}
策略工廠
public class AuditStrategyFactory {private final static AuditStrategyFactory factory = new AuditStrategyFactory();private List<AuditStrategy> auditStrategyList = new ArrayList<>();private AuditStrategyFactory() {auditStrategyList.add(new AuditStrategyImpl_1());auditStrategyList.add(new AuditStrategyImpl_2());auditStrategyList.add(new AuditStrategyImpl_3());// Add more strategies here}public static AuditStrategyFactory getInstance() {return factory;}public AuditStrategy getAuditStrategy(LeaveForm leaveForm) {AuditStrategy auditStrategy = null;for (AuditStrategy strategy : auditStrategyList) {if (strategy.isSupport(leaveForm)) {if (auditStrategy == null || strategy.getPriority() > auditStrategy.getPriority()) {auditStrategy = strategy;}}}if (auditStrategy == null) {throw new RuntimeException("沒有匹配到請假審核規則");}return auditStrategy;}
}
業務類
public class LeaveServiceNew {public void audit(LeaveForm leaveForm) {AuditStrategyFactory factory = AuditStrategyFactory.getInstance();AuditStrategy strategy = factory.getAuditStrategy(leaveForm);strategy.audit(leaveForm);}
}
測試
public class Client {public static void main(String[] args) {LeaveServiceNew leaveServiceNew = new LeaveServiceNew();LeaveForm form1 = new LeaveForm(new Employee("李總經理", 9), "甲流發燒", 10, 0);leaveServiceNew.audit(form1);LeaveForm form2 = new LeaveForm(new Employee("打工人1", 2), "甲流發燒", 2, 0);leaveServiceNew.audit(form2);LeaveForm form3 = new LeaveForm(new Employee("打工人2", 3), "結婚", 2, 1);leaveServiceNew.audit(form3);LeaveForm form4 = new LeaveForm(new Employee("打工人3", 4), "請年假,休息休息", 5, 2);leaveServiceNew.audit(form4);}
}
添加新規則

如果需要添加新的年假規則,只需要創建新的策略類并添加到工廠中即可。

public class AuditStrategyImpl_6 implements AuditStrategy {@Overridepublic boolean isSupport(LeaveForm leaveForm) {return leaveForm.getType() == 2;}@Overridepublic void audit(LeaveForm leaveForm) {System.out.println(leaveForm);System.out.println("查詢您的剩余年假天數...");System.out.println("剩余年假還有6天, 進入審批流程");}@Overridepublic int getPriority() {return 0;}@Overridepublic String getName() {return "年假審批規則";}
}

在工廠類中添加新的策略:

private AuditStrategyFactory() {auditStrategyList.add(new AuditStrategyImpl_1());auditStrategyList.add(new AuditStrategyImpl_2());auditStrategyList.add(new AuditStrategyImpl_3());auditStrategyList.add(new AuditStrategyImpl_6()); // 新添加的年假規則// Add more strategies here
}

通過這種方式,已經成功消除了 if-else 結構,每當新來了一種請假規則,只需要添加新的規則處理策略,并修改工廠中的集合。如果要使得程序符合開閉原則,可以通過反射機制,動態地加載策略類。

使用反射機制動態加載策略類
public class AuditStrategyFactory {private final static AuditStrategyFactory factory = new AuditStrategyFactory();private List<AuditStrategy> auditStrategyList = new ArrayList<>();private AuditStrategyFactory() {// 動態加載策略類try {String packageName = "com.example.strategies"; // 策略類所在包名ClassLoader classLoader = Thread.currentThread().getContextClassLoader();String path = packageName.replace('.', '/');Enumeration<URL> resources = classLoader.getResources(path);List<File> dirs = new ArrayList<>();while (resources.hasMoreElements()) {URL resource = resources.nextElement();dirs.add(new File(resource.getFile()));}for (File directory : dirs) {auditStrategyList.addAll(findClasses(directory, packageName));}} catch (Exception e) {e.printStackTrace();}}private List<AuditStrategy> findClasses(File directory, String packageName) throws ClassNotFoundException, InstantiationException, IllegalAccessException {List<AuditStrategy> strategies = new ArrayList<>();if (!directory.exists()) {return strategies;}File[] files = directory.listFiles();for (File file : files) {if (file.isDirectory()) {strategies.addAll(findClasses(file, packageName + "." + file.getName()));} else if (file.getName().endsWith(".class")) {String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);Class<?> clazz = Class.forName(className);if (AuditStrategy.class.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers())) {strategies.add((AuditStrategy) clazz.newInstance());}}}return strategies;}public static AuditStrategyFactory getInstance() {return factory;}public AuditStrategy getAuditStrategy(LeaveForm leaveForm) {AuditStrategy auditStrategy = null;for (AuditStrategy strategy : auditStrategyList) {if (strategy.isSupport(leaveForm)) {if (auditStrategy == null || strategy.getPriority() > auditStrategy.getPriority()) {auditStrategy = strategy;}}}if (auditStrategy == null) {throw new RuntimeException("沒有匹配到請假審核規則");}return auditStrategy;}
}

通過這種方式,策略類可以動態地從指定包中加載,實現了真正的開閉原則。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/42035.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/42035.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/42035.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

打卡第4天----鏈表

通過學習基礎,發現我的基本功還得需要再練練,思路得再更加清晰明了,這樣子做算法題才能駕輕就熟。每天記錄自己的進步。 一、兩兩交換 題目編號:24 題目描述: 給你一個鏈表,兩兩交換其中相鄰的節點,并返回交換后鏈表的頭節點。你必須在不修改節點內部的值的情況下完成本…

[數據結構] 基于交換的排序 冒泡排序快速排序

標題&#xff1a;[數據結構] 基于交換的排序 冒泡排序&&快速排序 水墨不寫bug &#xff08;圖片來源于網絡&#xff09; 目錄 &#xff08;一&#xff09;冒泡排序 優化后實現&#xff1a; &#xff08;二&#xff09;快速排序 I、實現方法&#xff1a; &#…

opencv環境搭建-python

最近遇到了一些圖像處理的需求&#xff0c;所以需要學習一下opencv,來記錄一下我的學習歷程。 安裝numpy pip install -i https://pypi.tuna.tsinghua.edu.cn/simple numpy安裝matplotlib pip install -i https://pypi.tuna.tsinghua.edu.cn/simple matplotlib安裝opencv …

ctfshow web入門 web338--web344

web338 原型鏈污染 comman.js module.exports {copy:copy };function copy(object1, object2){for (let key in object2) {if (key in object2 && key in object1) {copy(object1[key], object2[key])} else {object1[key] object2[key]}}}login.js var express …

【ubuntu】掛載新磁盤

1、查看磁盤 sudo fdisk -l#Disk /dev/sdb: 4.0 TiB #Disk model: HNA641010BCF105 #Units: sectors of 1 * 512 512 bytes #Sector size (logical/physical): 512 bytes / 4096 bytes #I/O size (minimum/optimal): 4096 bytes / 4096 bytes #Disklabel type: gpt #Disk id…

python argparse模塊nargs用法

nargs 是 argparse 模塊中用來指定參數的數量的屬性。不同的 nargs 取值有不同的含義&#xff0c;下面是一些常用的用法&#xff1a; nargsNone (默認值)&#xff1a;表示該參數只能接收一個值。例如&#xff1a;--foo 123。 nargs?&#xff1a;表示該參數最多接收一個值。如…

gcc/g++的四步編譯

目錄 前言1.預處理&#xff08;進行宏替換&#xff09;2.編譯&#xff08;生成匯編&#xff09;3.匯編&#xff08;生成二進制文件&#xff09;4. 鏈接 &#xff08;生成可執行文件&#xff09;a. 動態庫 && 動態鏈接b. 靜態庫 && 靜態鏈接c. 驗證d. 動靜態鏈接…

技術實現路徑怎么寫?(Word項目技術路徑文檔參考)

軟件項目編寫技術實現路徑至關重要&#xff0c;因為它為項目團隊提供了清晰的開發藍圖。這一路徑明確了從項目啟動到交付各階段所需的技術方案、步驟及預期成果&#xff0c;有助于團隊統一認識&#xff0c;確保開發工作有序進行。同時&#xff0c;技術實現路徑有助于識別潛在的…

HetuEngine簡介

目錄 HetuEngine是什么&#xff1f; HetuEngine的特點以及使用場景 特點 使用場景 HetuEngine介紹 結構 近期用到了Hetu&#xff0c;了解下這個工具是起什么作用的。 HetuEngine是什么&#xff1f; 是引擎&#xff0c;設計是為了讓與當前的大數據生態完美融合的引擎&am…

本安防爆手機:危險環境下的安全通信解決方案

在石油化工、煤礦、天然氣等危險環境中&#xff0c;通信安全是保障工作人員生命安全和生產順利進行的關鍵。防爆智能手機作為專為這些環境設計的通信工具&#xff0c;提供了全方位的安全通信解決方案。 防爆設計與材料&#xff1a; 防爆智能手機采用特殊的防爆結構和材料&…

Mysql部署MHA高可用

部署前準備&#xff1a; mysql-8.0.27下載地址&#xff1a;https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.27-1.el7.x86_64.rpm-bundle.tar mha-manager下載地址&#xff1a;https://github.com/yoshinorim/mha4mysql-manager/releases/download/v0.58/mha4mysql-mana…

【Selenium】 使用save_screenshot截圖無法保存圖片

Selenium 使用save_screenshot截圖無法保存 代碼如下 from time import sleep from selenium import webdriver driver webdriver.Chrome() driver.maximize_window() driver.get(http://www.baidu.com) # 截取當前窗口&#xff0c;指定截圖圖片的保存位置 driver.save_scre…

為什么需要做網絡安全服務?

網絡安全服務之所以重要&#xff0c;是因為它在保護數字資產、維護企業運營、確保法規遵從、防范惡意行為以及建立信任等方面扮演著關鍵角色。以下是一些主要的理由&#xff1a; 保護核心資產和數據&#xff1a; 數字化轉型使得企業數據變得極其寶貴&#xff0c;包括知識產權、…

深度學習模型加密python版本

支持加密的模型: # torch、torch script、onnx、tensorrt 、torch2trt、tensorflow、tensorflow2tensorrt、paddlepaddle、paddle2tensorrt 深度學習推理模型通常以文件的形式進行保存&#xff0c;相應的推理引擎通過讀取模型文件并反序列化即可進行推理過程. 這樣一來&#…

數據庫——事務管理

title: 數據庫——事務管理 date: 2024-07-06 11:55:39 tags: 數據庫 categories: 數據庫 cover: /image/T1.jpg description: 數據庫的事務管理的相關知識 事務管理 事務管理是對一系列數據庫操作進行管理的過程&#xff0c;這些操作被視為一個不可分割的工作單元&#xff0…

20K Stars!一個輕量級的 JS 庫

大家好,我是CodeQi! 一位熱衷于技術分享的碼仔。 Driver.js 是一個輕量級的 JavaScript 庫,旨在幫助開發人員創建網站或應用程序的引導和教程。通過 Driver.js,您可以引導用戶了解網站的各個功能和使用方式。 Driver.js 提供了高度可定制的功能,使其能夠適應各種需求和…

寶塔-Linux模板常用命令-centos7

一、寶塔-Linux模板常用命令&#xff1a; 1.停止寶塔 /etc/init.d/bt stop 2.啟動寶塔 /etc/init.d/bt start 3.重啟寶塔 /etc/init.d/bt restart 4.卸載寶塔 /etc/init.d/bt stop && chkconfig --del bt && rm -f /etc/init.d/bt && rm -rf …

如何使用echart做K線圖

使用ECharts制作K線圖需要先引入ECharts的庫文件&#xff0c;然后通過調用相應的API來配置和渲染K線圖。以下是一個簡單的示例代碼&#xff1a; // 引入ECharts庫文件 <script src"https://cdn.jsdelivr.net/npm/echarts5.0.0/dist/echarts.min.js"></scri…

使用Python繪制和弦圖

使用Python繪制和弦圖 和弦圖效果代碼 和弦圖 和弦圖用于展示數據的多對多關系&#xff0c;適合用于社交網絡、交通流量等領域的分析。 效果 代碼 import pandas as pd import holoviews as hv from holoviews import opts hv.extension(bokeh)# 示例數據 data [(A, B, 2),…

想在vue中預覽doxc,excel,pdf文件? vue-office提供包支持

在浩瀚的Vue生態中&#xff0c;vue-office猶如一顆璀璨的星辰&#xff0c;以其獨特的魅力照亮了開發者處理多種文件格式的預覽之路。這款精心打造的Vue組件庫&#xff0c;不僅擁抱了Vue2的經典&#xff0c;也緊密跟隨Vue3的步伐&#xff0c;展現了卓越的技術前瞻性和兼容性。它…