一、问题引入
当我们导出一些数据到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()等方法。