作者:京東物流 楊唯一
一、Java 枚舉類
Java 枚舉是一個(gè)特殊的類,一般表示一組常量,比如一年的 4 個(gè)季節(jié),一年的 12 個(gè)月份,一個(gè)星期的 7 天,方向有東南西北等。
我們?cè)跇I(yè)務(wù)需求開發(fā)中,通常會(huì)使用枚舉來(lái)定義業(yè)務(wù)上的一組常量,那除了簡(jiǎn)單地定義常量之外,我們?nèi)绾卫妹杜e來(lái)實(shí)現(xiàn)高內(nèi)聚、低耦合的設(shè)計(jì)呢?下面介紹下枚舉和策略模式、函數(shù)式接口的組合應(yīng)用。
二、枚舉+策略模式
首先介紹下策略模式,已經(jīng)掌握策略模式的同學(xué)可以直接跳過(guò)此部分。
1、策略模式
策略模式(Strategy Pattern)屬于對(duì)象的行為模式。其用意是針對(duì)一組算法,將每一個(gè)算法封裝到具有共同接口的獨(dú)立的類中,從而使得它們可以相互替換。策略模式使得算法可以在不影響到客戶端的情況下發(fā)生變化。 其主要目的是通過(guò)定義相似的算法,替換if else 語(yǔ)句寫法,并且可以隨時(shí)相互替換。
策略模式主要由這三個(gè)角色組成,環(huán)境角色(Context)、抽象策略角色(Strategy)和具體策略角色(ConcreteStrategy)。
?環(huán)境角色(Context):持有一個(gè)策略類的引用,提供給客戶端使用。
?抽象策略角色(Strategy):這是一個(gè)抽象角色,通常由一個(gè)接口或抽象類實(shí)現(xiàn)。此角色給出所有的具體策略類所需的接口。
?具體策略角色(ConcreteStrategy):包裝了相關(guān)的算法或行為。
這里為了方便理解,我們就拿剛學(xué)習(xí)Java的時(shí)候使用計(jì)算方法來(lái)說(shuō)吧。 在使用計(jì)算器進(jìn)行計(jì)算的時(shí)候,會(huì)經(jīng)常用到加減乘除方法。如果我們想得到兩個(gè)數(shù)字相加的和,我們需要用到“+”符號(hào),得到相減的差,需要用到“-”符號(hào)等等。雖然我們可以通過(guò)字符串比較使用if/else寫成通用方法,但是計(jì)算的符號(hào)每次增加,我們就不得不加在原先的方法中進(jìn)行增加相應(yīng)的代碼,如果后續(xù)計(jì)算方法增加、修改或刪除,那么會(huì)使后續(xù)的維護(hù)變得困難。 但是在這些方法中,我們發(fā)現(xiàn)其基本方法是固定的,這時(shí)我們就可以通過(guò)策略模式來(lái)進(jìn)行開發(fā),可以有效避免通過(guò)if/else來(lái)進(jìn)行判斷,即使后續(xù)增加其他的計(jì)算規(guī)則也可靈活進(jìn)行調(diào)整。
首先定義一個(gè)抽象策略角色,并擁有一個(gè)計(jì)算的方法。
interface CalculateStrategy { int doOperation(int num1, int num2); }
然后再定義加減乘除這些具體策略角色并實(shí)現(xiàn)方法。
代碼如下:
class OperationAdd implements CalculateStrategy { @Override public int doOperation(int num1, int num2) { return num1 + num2; } } class OperationSub implements CalculateStrategy { @Override public int doOperation(int num1, int num2) { return num1 - num2; } } class OperationMul implements CalculateStrategy { @Override public int doOperation(int num1, int num2) { return num1 * num2; } } class OperationDiv implements CalculateStrategy { @Override public int doOperation(int num1, int num2) { return num1 / num2; } }
最后在定義一個(gè)環(huán)境角色,提供一個(gè)計(jì)算的接口供客戶端使用。 代碼如下:
class CalculatorContext { private CalculateStrategy strategy; public CalculatorContext(CalculateStrategy strategy) { this.strategy = strategy; } public int executeStrategy(int num1, int num2) { return strategy.doOperation(num1, num2); } }
編寫好之后,那么我們來(lái)進(jìn)行測(cè)試。 測(cè)試代碼如下:
public static void main(String[] args) { int a=4,b=2; CalculatorContext context = new CalculatorContext(new OperationAdd()); System.out.println("a + b = "+context.executeStrategy(a, b)); CalculatorContext context2 = new CalculatorContext(new OperationSub()); System.out.println("a - b = "+context2.executeStrategy(a, b)); CalculatorContext context3 = new CalculatorContext(new OperationMul()); System.out.println("a * b = "+context3.executeStrategy(a, b)); CalculatorContext context4 = new CalculatorContext(new OperationDiv()); System.out.println("a / b = "+context4.executeStrategy(a, b)); }
輸出結(jié)果:
a + b = 6 a - b = 2 a * b = 8 a / b = 2
上面這段是網(wǎng)上常見的對(duì)于策略模式的介紹和示例,但在這段測(cè)試代碼中,我們對(duì)于策略的選擇依然是在具體的調(diào)用處通過(guò)對(duì)CalculatorContext類構(gòu)造器的傳參去指定的,那么如何能動(dòng)態(tài)地選擇策略并將選擇策略的具體邏輯抽取,就要用到枚舉+策略這套組合拳啦。
2、枚舉+策略模式的使用
定義策略模式的枚舉類,并將具體策略的 bean 名稱作為枚舉類 strategy 的值;
在getStrategyEnum方法中,我們可以去實(shí)現(xiàn)選擇策略的邏輯,將選擇策略的判斷邏輯內(nèi)聚在枚舉類中,與業(yè)務(wù)代碼隔離,當(dāng)然在具體業(yè)務(wù)中,我們對(duì)于策略選擇的判斷會(huì)更復(fù)雜,此處只是舉個(gè)簡(jiǎn)單的例子說(shuō)明下:
@Getter public enum CalculateStrategyEnum { ADD("operationAdd"), SUB("operationSub"), MUL("operationMul"), DIV("operationDiv"); private final String strategy; CalculateStrategyEnum(String strategy) { this.strategy = strategy; } public static CalculateStrategyEnum getStrategyEnum(String operationName) { switch (operationName) { case "加法運(yùn)算": return ADD; case "減法運(yùn)算": return SUB; case "乘法運(yùn)算": return MUL; case "除法運(yùn)算": return DIV; default: return null; } } }
策略實(shí)現(xiàn)類中加入@Component 注解,將 bean 實(shí)例交給 spring 容器管理
@Component class OperationAdd implements CalculateStrategy { @Override public int doOperation(int num1, int num2) { return num1 + num2; } } @Component class OperationSub implements CalculateStrategy { @Override public int doOperation(int num1, int num2) { return num1 - num2; } } @Component class OperationMul implements CalculateStrategy { @Override public int doOperation(int num1, int num2) { return num1 * num2; } } @Component class OperationDiv implements CalculateStrategy { @Override public int doOperation(int num1, int num2) { return num1 / num2; } }
策略調(diào)用處:業(yè)務(wù)處理
@Service class CalculateService { // 注入所有策略類 @Resource Map calculateStrategy; public executeStrategy(String operationName, int num1, int num2) { // 根據(jù)參數(shù)匹配對(duì)應(yīng)的枚舉實(shí)例 CalculateStrategyEnum strategy = CalculateStrategyEnum.getStrategyEnum(operationName); // 獲取對(duì)應(yīng)bean執(zhí)行策略算法 if (stategy != null) { calculateStrategy.get(strategy.getStrategy()).doOperation(num1, num2) } } }
當(dāng)我們需要新增算法時(shí),只需新增新的策略實(shí)現(xiàn)類和枚舉實(shí)例,修改枚舉類中的策略選擇方法,無(wú)需入侵現(xiàn)有業(yè)務(wù)代碼,實(shí)現(xiàn)了高內(nèi)聚、低耦合,增強(qiáng)了系統(tǒng)的靈活性和可擴(kuò)展性。
三、枚舉+函數(shù)式接口
1、函數(shù)式接口
什么是函數(shù)式接口
函數(shù)式接口是只包含一個(gè)抽象方法的接口。但是默認(rèn)方法和靜態(tài)方法在此接口中可以定義多個(gè)。Java 中的函數(shù)式接口可以被用作 Lambda 表達(dá)式的目標(biāo)類型。通過(guò)函數(shù)式接口,可以實(shí)現(xiàn)更簡(jiǎn)潔、更具可讀性的代碼,從而支持函數(shù)式編程的思想。
Java 中有一些內(nèi)置的函數(shù)式接口,用于不同的用途:
1.Runnable: 用于描述可以在單獨(dú)線程中執(zhí)行的任務(wù)。
2.Callable: 類似于 Runnable,但可以返回執(zhí)行結(jié)果或拋出異常。
3.Comparator: 用于比較兩個(gè)對(duì)象的順序。
4.Function: 接受一個(gè)參數(shù)并產(chǎn)生一個(gè)結(jié)果。
5.Predicate: 接受一個(gè)參數(shù)并返回一個(gè)布爾值,用于判斷條件是否滿足。
6.Supplier: 不接受參數(shù),但返回一個(gè)值。
7.Consumer: 接受一個(gè)參數(shù),無(wú)返回值。
2、枚舉+函數(shù)式接口的使用
我們可以通過(guò)為枚舉類設(shè)置函數(shù)式接口類型的屬性,讓枚舉實(shí)例擁有一些特定的行為。
例如下面的例子,通過(guò)Runnable接口為枚舉實(shí)例賦予了對(duì)應(yīng)的行為。
public enum ActionEnum { RUN(ActionEnum::run), JUMP(ActionEnum::jump), SIT(ActionEnum::sit); private final Runnable action; ActionEnum(Runnable action) { this.action = action; } public static void run() { System.out.println("Running"); } public static void jump() { System.out.println("Jumping"); } public static void sit() { System.out.println("Sitting"); } }
也可以根據(jù)具體行為是否消費(fèi)參數(shù),以及是否提供返回值來(lái)選擇合適的接口
public enum ActionEnum { RUN(ActionEnum::run), JUMP(ActionEnum::jump), SIT(ActionEnum::sit); private final Supplier action; ActionEnum(Supplier action) { this.action = action; } public static String run() { return "Running"; } public static String jump() { return "Jumping"; } public static String sit() { return "Sitting"; } }
這樣,我們可以將與枚舉值強(qiáng)關(guān)聯(lián)的一些行為封裝到枚舉類中,與其他業(yè)務(wù)代碼隔離,實(shí)現(xiàn)高內(nèi)聚、低耦合的設(shè)計(jì)。
引用:
策略模式介紹:
https://www.cnblogs.com/xuwujing/p/9954263.html
審核編輯 黃宇
-
接口
+關(guān)注
關(guān)注
33文章
8496瀏覽量
150831 -
JAVA
+關(guān)注
關(guān)注
19文章
2957瀏覽量
104544
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論