1. 什么是策略模式(Strategy Pattern)
定義
策略模式(Strategy Pattern)的原始定義是:定義一系列算法,將每一個算法封裝起來,并使它們可以相互替換。策略模式讓算法可以獨立于使用它的客戶端而變化。
目的
策略模式的目的是在軟件開發中,當實現某一個功能存在多種算法或者策略時,可以根據環境或者條件的不同選擇不同的算法或者策略來完成該功能。
比如網購,你可以選擇工商銀行、農業銀行、建設銀行等等,但是它們提供的算法都是一致的,就是幫你付款。
角色
策略模式的主要角色如下:
- 抽象策略(Strategy)類:這是一個抽象角色,通常由一個接口或抽象類實現。此角色給出所有的具體策略類所需的接口。
- 具體策略(Concrete Strategy)類:實現了抽象策略定義的接口,提供具體的算法實現或行為。
- 環境或上下文(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.優缺點
優點
- 易于擴展和維護:由于不同的算法被封裝在不同的類中,所以我們可以很容易地添加新的算法或修改已有算法,而不需要修改客戶端的代碼。
- 提高代碼的可讀性:將不同的算法封裝在不同的類中,使得代碼更加模塊化,易于理解和維護。
- 消除大量的條件語句:使用策略模式,我們可以將不同的算法替換成不同的類,從而消除大量的if-else語句,使得代碼更加簡潔和易于理解。
缺點
- 需要額外的類和接口:使用策略模式,我們需要為每個算法都創建一個獨立的類,從而增加了代碼的復雜度。
- 客戶端需要知道所有的策略類:使用策略模式,客戶端需要知道所有的策略類,以便在運行時選擇合適的策略。這可能會增加代碼的復雜度。
應用場景
策略模式適用于以下場景:
- 需要根據不同的條件選擇不同的算法時:例如,計算器程序需要根據用戶輸入的運算符選擇相應的計算方法。
- 需要在運行時動態地選擇算法時:例如,某個系統需要根據用戶的配置或環境變量來選擇合適的算法。
- 需要將算法的實現細節與客戶端代碼分離時:例如,某個系統需要根據不同的數據來源來解析數據,但是客戶端并不關心數據的解析細節。
總結
策略模式通過定義一系列算法,將每一個算法封裝起來,并使它們可以相互替換,從而讓算法可以獨立于使用它的客戶端而變化。通過使用策略模式,可以提高代碼的擴展性、可讀性和維護性,同時也可以消除大量的條件語句。
在工作中,為了消除代碼中的大量 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;}
}
通過這種方式,策略類可以動態地從指定包中加載,實現了真正的開閉原則。