2025年了一行实现策略模式

994 阅读6分钟

纳 ~ 有码有图有真相

下面是测试样例以及测试结果

@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());
}

结果截图