前言
在 设计模式(一)策略模式 —— 策略模式结构 博文中分析了策略模式的基础结构,最后也提到了一点弊端,就是说 调用方依赖具体策略类 ,这篇博文演示通过枚举类使用策略模式。
场景
通过枚举类使用策略模式适用于策略计算逻辑不和其他模块耦合的场景,比如一些年月日周期的计算逻辑,只是单纯的时间进行计算,可以将策略类放在枚举中。但是如果策略和多个模块有耦合关系就不适用,但可以springIOC实现,下篇博文分析。
场景是计算时间策略,如获取当前周期的结束时间。
类图
类图中简略的写了月周期和季度周期
代码实现
定义周期策略接口
/**
* 周期计算策略
*
* @author lishuzhen
* @date 2022/5/20 19:33
*/
public interface CycleStrategy {
/**
* 根据开始时间计算结束时间
*
* @param startTime
* @return
*/
Date figureEndTime(Date startTime);
}
实现周期策略接口,定义具体策略计算逻辑
/**
* 周期计算策略 月
*
* @author lishuzhen
* @date 2022/5/20 19:57
*/
public class MonthCycleStrategy implements CycleStrategy {
/**
* 根据开始时间计算结束时间
*
* @param startTime
* @return
*/
@Override
public Date figureEndTime(Date startTime) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(startTime);
calendar.set(Calendar.DATE, calendar.getActualMaximum(Calendar.DATE));
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.SECOND, 59);
calendar.set(Calendar.MINUTE, 59);
return calendar.getTime();
}
}
/**
* 周期计算策略 季度
*
* @author lishuzhen
* @date 2022/5/20 19:47
*/
public class QuarterlyCycleStrategy implements CycleStrategy {
/**
* 根据开始时间计算结束时间
*
* @param startTime
* @return
*/
@Override
public Date figureEndTime(Date startTime) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(startTime);
int currentMonth = calendar.get(Calendar.MONTH) + 1;
if (currentMonth >= 1 && currentMonth <= 3) {
calendar.set(Calendar.MONTH, 2);
calendar.set(Calendar.DATE, 31);
} else if (currentMonth >= 4 && currentMonth <= 6) {
calendar.set(Calendar.MONTH, 5);
calendar.set(Calendar.DATE, 30);
} else if (currentMonth >= 7 && currentMonth <= 9) {
calendar.set(Calendar.MONTH, 8);
calendar.set(Calendar.DATE, 30);
} else if (currentMonth >= 10 && currentMonth <= 12) {
calendar.set(Calendar.MONTH, 11);
calendar.set(Calendar.DATE, 31);
}
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.SECOND, 59);
calendar.set(Calendar.MINUTE, 59);
return calendar.getTime();
}
}
定义策略上下文对象
/**
* 周期策略计算上下文
*
* @author lishuzhen
* @date 2022/5/20 20:02
*/
public class CycleStrategyContext {
private CycleStrategy cycleStrategy;
public CycleStrategyContext(String cycleTypeCode) {
this.cycleStrategy = CycleTypeEnum.getByCode(cycleTypeCode).getCycleStrategy();
}
/**
* 根据开始时间计算结束时间
*
* @param startTime
* @return
*/
public Date figureEndTime(Date startTime) {
return cycleStrategy.figureEndTime(startTime);
}
}
定义策略枚举
/**
* 周期类型枚举
*
* @author lishuzhen
* @date 2022/5/20 19:46
*/
public enum CycleTypeEnum {
DAY("0", "日", null),
WEEK("1", "周", null),
MONTH("2", "月", new MonthCycleStrategy()),
QUARTERLY("3", "季度", new QuarterlyCycleStrategy()),
YEAR("4", "年", null),
;
private String code;
private String description;
private CycleStrategy cycleStrategy;
CycleTypeEnum(String code, String description, CycleStrategy cycleStrategy) {
this.code = code;
this.description = description;
this.cycleStrategy = cycleStrategy;
}
public static CycleTypeEnum getByCode(String code) {
for (CycleTypeEnum value : CycleTypeEnum.values()) {
if (value.code.equals(code)) {
return value;
}
}
throw new IllegalArgumentException(String.format("周期类型 参数不合法 code = %s" + code));
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
public CycleStrategy getCycleStrategy() {
return cycleStrategy;
}
}
调用方
System.out.println( "这个季度的最后一天是:" + new CycleStrategyContext(CycleTypeEnum.QUARTERLY.getCode()).figureEndTime(new Date()));
总结
优点
- 通过枚举使用策略模式时调用方无需知道具体的策略实现。
- 适用于所有策略类都高内聚在自身的模块中。因为枚举类类中要存放具体的策略类。
缺点
- 不适用于具体策略类分散在多个模块中的计算。比如订单类型策略计算,枚举类要和秒杀、促销等等多个模块耦合,这样并不是一个很好的设计。但是对于这一点可以通过spring的IOC解决,这也是实际开发中应用最多一种方式,在下一篇博文中讲解。