一、問題引入
當我們導出一些數據到Excel表格時,有時候需要從不同的維度導出(如:個人維度,時間維度),不同維度所需的處理方式不同,此時的場景很適合用策略模式,下面就以不同維度導出Excel表格為例介紹策略模式。
二、策略模式概念和理論知識
2.1,策略模式的概念
在策略模式(Strategy Pattern)中,一個類的行為或其算法可以在運行時更改。我們創建表示各種策略的對象和一個行為隨着策略對象改變而改變的 context 對象。策略對象改變 context 對象的執行算法。
策略模式是一種行為模式,也是替代⼤量 if...else...的利器。它所能幫你解決的是場景,一般是具有同類可替代的行為邏輯算法場景。例如:不同類型的交易行式(信用卡、支付寶、微信)、生成唯一ID策略 (UUID、DB自增、DB+Redis、雪花算法、Leaf算法)等,都可以使用策略模式進行行為包裝,供給外部使用。
2.2,策略模式類圖

(圖片取自百度百科)
2.3,策略模式的優缺點
優點是:1,降低耦合性;2,代替if...else...,避免使用多重條件判斷;3,符合開閉原則,擴展性良好。
缺點是: 1、策略類會增多。 2、所有策略類都需要對外暴露。
三、策略模式的應用
業務場景:假設一個問卷系統收集了很多用戶的答卷,現在需要從兩個維度導出用戶答卷,分別是:用戶維度,時間維度。
我們首先創建一個抽象策略類:BaseExportHandler
1 /** 2 * @Version V1.0.0 3 * @Description 數據導出抽象策略類 4 */ 5 public abstract class BaseExportHandler { 6 7 /** 8 * 導出 9 * 10 * @param startTime 開始時間 11 * @param endTime 結束時間 12 * @param userId 問卷Id 13 */ 14 public abstract Boolean doExport(Date startTime, Date endTime, Long userId); 15 }
抽象策略類中有一個抽象方法:doExport(),定義一下導出方法。
然后創建用戶維度導出策略類:UserExportHandler,時間維度導出策略類:TimeExportHandler,這兩個類是抽象策略類的實現
1 /** 2 * @Version 1.0 3 * @Description 個人維度導出策略 4 */ 5 public class UserExportHandler extends BaseExportHandler { 6 /** 7 * 導出 8 * 9 * @param startTime 開始時間 10 * @param endTime 結束時間 11 * @param userId 問卷Id 12 */ 13 @Override 14 public Boolean doExport(Date startTime, Date endTime, Long userId) { 15 System.out.println("個人維度導出成功..."); 16 return true; 17 } 18 }
/** * @Version 1.0 * @Description 時間維度導出策略 */ public class TimeExportHandler extends BaseExportHandler { /** * 導出 * * @param startTime 開始時間 * @param endTime 結束時間 * @param userId 問卷Id */ @Override public Boolean doExport(Date startTime, Date endTime, Long userId) { System.out.println("時間維度導出成功..."); return null; } }
創建一個枚舉:ExportTypeEnum
1 /** 2 * @Version 1.0 3 * @Description 導出類型枚舉 4 */ 5 public enum ExportTypeEnum { 6 User(UserExportHandler.class.getSimpleName(), new UserExportHandler()), 7 Time(TimeExportHandler.class.getSimpleName(), new TimeExportHandler()) 8 ; 9 10 private final String exportType; 11 private final BaseExportHandler exportHandler; 12 ExportTypeEnum(String exportType, BaseExportHandler exportHandler) { 13 this.exportType = exportType; 14 this.exportHandler = exportHandler; 15 } 16 17 /** 18 * 匹配 19 */ 20 public static ExportTypeEnum match(String exportType) { 21 ExportTypeEnum[] values = ExportTypeEnum.values(); 22 for (ExportTypeEnum typeEnum : values) { 23 if (typeEnum.exportType.equals(exportType)) { 24 return typeEnum; 25 } 26 } 27 return null; 28 } 29 30 public String getExportType() { 31 return exportType; 32 } 33 34 public BaseExportHandler getExportHandler() { 35 return exportHandler; 36 } 37 }
創建一個測試,我們試一下
1 public class TestExport { 2 public static void main(String[] args) { 3 String exportType = UserExportHandler.class.getSimpleName(); 4 ExportTypeEnum typeEnum = ExportTypeEnum.match(exportType); 5 BaseExportHandler exportHandler = typeEnum.getExportHandler(); 6 if (exportHandler != null) { 7 exportHandler.doExport(new Date(), new Date(), 1L); 8 } 9 } 10 }
輸出
1 時間維度導出成功... 2 3 Process finished with exit code 0
通過使用策略模式,替換了if...else...從而使得不同策略之間不需要融合到一起,防止其中一個策略出問題后影響其它策略,同時提高了系統整體的可擴展性,最大程度上實現開閉原則。
在Java8中使用策略模式舉例:
java.lang.CharacterData類,這個類使用的就是策略模式,其static final CharacterData of(int ch)方法是策略邏輯方法,
java.lang.Character類是CharacterData類的調用類,具體使用的方法可參考 isWhitespace()、getType()等方法。
