纳 ~ 有码有图有真相
下面是测试样例以及测试结果
@Test
void testSend() {
service.send("心中无女人,编码自然神", ESmsSendStatus.WAIT.getType());
}
调用等待中的对应策略实现
接下来我们进入真正实操体验
定义顶层策略接口
设计一个顶层策略接口,这个接口是需要进行功能实现的入口,你需要实现什么功能,在这个接口里面定义,需要注意接口中的职责不建议太多。
public interface ISmsEventHandler {
/**
* 处理短信
*
* @param msg 短信内容
*/
void handle(String msg);
}
定义抽象策略类
设计一个实现了顶层接口的抽象策略类,这个抽象策略类里面做一些抽象类的子类的公共操作,子类只需要关注各自的不同实现,差异化分布到各个子类,通用性留在父类。
public abstract class AbstractSmsStatusEventHandle implements ISmsEventHandler {
/**
* 处理短信
*
* @param msg 短信内容
*/
@Override
public void handle(String msg) {
// 这里定义公共操作
// 这里调用子类处理方法
handleEvent(msg);
}
/**
* 处理短信
*
* @param msg 短信内容
*/
protected abstract void handleEvent(String msg);
}
定义策略实现类
我们开始定义抽象类的各个实现类,我们来想一下,发送短信一共有几个状态?提交申请、发送中、发送成功、发送失败,没错一共有四种状态,所以我们需要定义四种状态的实现类。
定义提交申请策略
@Component
public class SmsWaitEventHandle extends AbstractSmsStatusEventHandle {
/**
* 处理短信
*
* @param msg 短信内容
*/
@Override
protected void handleEvent(String msg) {
// 这里是处理短信等待中逻辑
System.out.println("这里是短信提交请求中逻辑:" + msg);
}
}
定义发送中策略
@Component
public class SmsSendingEventHandle extends AbstractSmsStatusEventHandle{
/**
* 处理短信
*
* @param msg 短信内容
*/
@Override
protected void handleEvent(String msg) {
// 这里是短信发送中处理逻辑
System.out.println("这里是短信发送中处理逻辑:" + msg);
}
}
定义发送失败策略
@Component
public class SmsFailedEventHandle extends AbstractSmsStatusEventHandle{
/**
* 处理短信
*
* @param msg 短信内容
*/
@Override
protected void handleEvent(String msg) {
// 这里是处理短信失败的逻辑
System.out.println("这里是短信发送失败的逻辑:" + msg);
}
}
定义发送成功策略
@Component
public class SmsSucceedEventHandle extends AbstractSmsStatusEventHandle {
/**
* 处理短信
*
* @param msg 短信内容
*/
@Override
protected void handleEvent(String msg) {
// 这里是处理短信成功的逻辑
System.out.println("这里是短信发送成功的逻辑:" + msg);
}
}
定义枚举
枚举支持天然的策略模式,我们只需要在枚举中的每个状态声明各自的策略类型也就是策略.class文件,再定义一个方法,利用Hutool的SpringUtil通过枚举实例的策略class文件获取对应的实例Bean,比如这里我们拿到了WAIT这个枚举实例,我们通过WAIT.handle(msg),即可调到SmsWaitEventHandle的handle方法,真正实现每一个类型对应每一种策略。
核心枚举
@SuppressWarnings("AlibabaEnumConstantsMustHaveComment")
@Getter
@AllArgsConstructor
public enum ESmsSendStatus implements IDictEnum {
WAIT(0, "提交请求", SmsWaitEventHandle.class),
SUCCESS(1, "发送成功", SmsSucceedEventHandle.class),
FAILED(2, "发送失败", SmsFailedEventHandle.class),
SENDING(3, "发送中", SmsSendingEventHandle.class),
;
private final Integer type;
private final String desc;
private final Class<? extends ISmsEventHandler> clazz;
public void handle(String msg) {
SpringUtil.getBean(clazz).handle(msg);
}
}
枚举接口
这是一种枚举接口封装,很值得研究,陆总的神作。
public interface IDictEnum {
/**
* 缓存已获取的枚举数据
*/
Map<Class<?>, Map<Integer, Enum<?>>> enumCache = new ConcurrentHashMap<>();
/**
* 获取类型
*/
Integer getType();
/**
* 获取描述
*/
String getDesc();
/**
* 获取类型字符串
*
* @return 类型字符串
*/
default String getTypeString() {
Integer type = getType();
return ObjectUtil.isNull(type) ? null : type.toString();
}
/**
* 根据类型获取类型描述
*
* @param type 类型
* @param clazz 类
* @param <E> 泛型
* @return 枚举
*/
static <E extends Enum<E> & IDictEnum> E getEnum(Integer type, Class<E> clazz) {
if (type == null || clazz == null) {
return null;
}
// 尝试从缓存中获取
Map<Integer, Enum<?>> cachedEnums = enumCache.computeIfAbsent(clazz, k -> new ConcurrentHashMap<>());
Enum<?> cachedEnum = cachedEnums.get(type);
if (cachedEnum != null) {
return clazz.cast(cachedEnum);
}
EnumSet<E> all = EnumSet.allOf(clazz);
E result = all.stream().filter(e -> e.getType().equals(type)).findFirst().orElse(null);
if (result != null) {
cachedEnums.put(type, result);
}
return result;
}
/**
* 根据类型获取类型描述
*
* @param type 类型
* @param clazz 类
* @param <E> 泛型
* @return 枚举
*/
static <E extends Enum<E> & IDictEnum> E getEnum(String type, Class<E> clazz) {
return getEnum(Integer.parseInt(type), clazz);
}
/**
* 如果为空则返回默认的字典
*
* @param type 类型
* @param clazz 类
* @param defaultDict 默认值
* @param <E> 泛型
* @return 枚举
*/
static <E extends Enum<E> & IDictEnum> E defaultIfNull(Integer type, Class<E> clazz, E defaultDict) {
return ObjectUtil.defaultIfNull(getEnum(type, clazz), defaultDict);
}
/**
* 获取类型获取类型编号
*
* @param desc 描述
* @param clazz 类
* @param <E> 泛型
* @return 枚举
*/
static <E extends Enum<E> & IDictEnum> E getEnumByDesc(String desc, Class<E> clazz) {
if (desc == null || clazz == null) {
return null;
}
EnumSet<E> all = EnumSet.allOf(clazz);
return all.stream().filter(e -> e.getDesc().equals(desc)).findFirst().orElse(null);
}
/**
* 获取类型获取类型编号
*
* @param type 类型
* @param clazz 类
* @param <E> 泛型
* @return 枚举
*/
static <E extends Enum<E> & IDictEnum> String getDescByType(Integer type, Class<E> clazz) {
E dict = getEnum(type, clazz);
if (dict == null) {
return "未知";
}
return dict.getDesc();
}
/**
* 判断两个字典枚举是否一致
*
* @param e1 字典1
* @param e2 字典2
* @return 是否一致
*/
static boolean eq(IDictEnum e1, IDictEnum e2) {
if (ObjectUtil.isNull(e1) && ObjectUtil.isNull(e2)) {
return true;
}
if (ObjectUtil.isNull(e1) || ObjectUtil.isNull(e2)) {
return false;
}
return e1.equals(e2);
}
/**
* 判断type是否和字典一样的类型
*
* @param type type
* @param e 字典
* @return 是否一致
*/
static boolean eq(Integer type, IDictEnum e) {
if (ObjectUtil.isNull(type) && ObjectUtil.isNull(e)) {
return true;
}
if (ObjectUtil.isNull(type) || ObjectUtil.isNull(e)) {
return false;
}
return type.equals(e.getType());
}
/**
* 判断两个字典枚举是否不同
*
* @param e1 字典1
* @param e2 字典2
* @return 是否不同
*/
static boolean ne(@NonNull IDictEnum e1, @NonNull IDictEnum e2) {
return !eq(e1, e2);
}
/**
* 判断两个字典枚举是否不同
*
* @param type 类型
* @param e 字典
* @return 是否不同
*/
static boolean ne(Integer type, IDictEnum e) {
return !eq(type, e);
}
/**
* 判断多个字典枚举是否不同
*
* @param type 类型
* @param es 字典
* @return 是否不同
*/
static boolean ne(Integer type, IDictEnum... es) {
return es != null && es.length > 0 && Arrays.stream(es).allMatch(e -> ne(type, e));
}
/**
* 判断该枚举值是否为空
*
* @param type 类型
* @param clazz 类
* @return 是否为空
*/
static <E extends Enum<E> & IDictEnum> boolean isNull(Integer type, Class<E> clazz) {
return ObjectUtil.isNull(getEnum(type, clazz));
}
/**
* 当type是否和字典是一样的类型的时候执行指定函数
*
* @param type 类型
* @param e 字典
* @param func 函数引用
*/
static void handleIfEqual(Integer type, IDictEnum e, Runnable func) {
if (eq(type, e)) {
func.run();
}
}
/**
* 当type是否和字典是一样的类型的时候执行指定函数
*
* @param e1 字典1
* @param e2 字典2
* @param func 函数引用
*/
static void handleIfEqual(IDictEnum e1, IDictEnum e2, Runnable func) {
if (eq(e1, e2)) {
func.run();
}
}
/**
* 如果是该枚举则执行函数调用
*
* @param type 类型
* @param func 函数引用
*/
default void handleIf(Integer type, Runnable func) {
if (eq(type, this)) {
func.run();
}
}
/**
* 如果是该枚举则执行函数调用
*
* @param dict 字典
* @param func 函数调用
*/
default void handleIf(IDictEnum dict, Runnable func) {
if (eq(dict, this)) {
func.run();
}
}
}
调用入口
这是正常是Service了,也就是调用状态调用策略的入口。
服务类接口
正常定义
public interface ISmsService {
/**
* 发送短信
* @param msg 短信内容
*/
void send(String msg, Integer status);
}
服务类实现
这里我们只需要一行既可实现,调用传入类型对应的策略实现
@Service
public class SmsServiceImpl implements ISmsService {
/**
* 发送短信
*
* @param msg 短信内容
* @param status
*/
@Override
public void send(String msg, Integer status) {
IDictEnum.getEnum(status, ESmsSendStatus.class).handle(msg);
}
}
测试
随着传入的类型不同,执行不同的策略,实现优雅的策略模式,陆总可谓真是枚举神仙。
测试状态为提交
单元测试
@Test
void testSend() {
service.send("心中无女人,编码自然神", ESmsSendStatus.WAIT.getType());
}
结果截图
测试状态为处理
单元测试
@Test
void testSend() {
service.send("心中无女人,编码自然神", ESmsSendStatus.SENDING.getType());
}
结果截图
测试状态为成功
单元测试
@Test
void testSend() {
service.send("心中无女人,编码自然神", ESmsSendStatus.SUCCESS.getType());
}
结果截图
测试状态为失败
单元测试
@Test
void testSend() {
service.send("心中无女人,编码自然神", ESmsSendStatus.FAILED.getType());
}
结果截图