使用策略模式优化你的代码

策略模式简介

策略模式(Strategy Pattern:Define a family of algorithms,encapsulate each one,and make them interchangeable.)中文解释为:定义一组算法,然后将这些算法封装起来,以便它们之间可以互换,属于一种对象行为型模式。总的来说策略模式是一种比较简单的模式,听起来可能有点费劲,其实就是定义一组通用算法的上层接口,各个算法实现类实现该算法接口,封装模块使用类似于 Context 的概念,Context 暴漏一组接口,Context 内部接口委托到抽象算法层。

策略模式的通用类图如下:

包含的角色如下:

  • 上下文角色(Context):该角色一般是一个实现类或者封装类,起到一定的封装及隔离作用,实际接受请求并将请求委托给实际的算法实现类处理,避免外界对底层策略的直接访问;
  • 抽象策略角色(Strategy):该角色一般是一个抽象角色,为接口或者抽象类扮演,定义具体策略角色的公共接口;
  • 具体策略角色(ConcreteStrategy):实现抽象策略角色的接口,为策略的具体实现类。

使用策略模式优化代码逻辑判断

假设我们有个积分发放功能,需要根据不同的积分类型来发分,我们的积分类型假设如下:

java 复制代码
/**
 * 积分类型枚举
 */
public enum PointTypeEnum {
    COURSE_LEARN,
    COURSE_COMMENT,
    EBOOK_READ,
    LIVE_WATCH,
}

然后我们实现一个发放积分的方法:

java 复制代码
/**
 * 指定积分类型,发分
 * @param pointTypeEnum 积分类型
 * @param points 要发的积分
 */
private static void sendPoint(PointTypeEnum pointTypeEnum, Integer points) {
    switch (pointTypeEnum) {
        case COURSE_COMMENT:
            log.info("评论课程,获得 {} 积分", points);
            break;
        case COURSE_LEARN:
            log.info("学习课程,获得 {} 积分", points);
            break;
        case EBOOK_READ:
            log.info("阅读电子书,获得 {} 积分", points);
            break;
        case LIVE_WATCH:
            log.info("观看直播,获得 {} 积分", points);
            break;
        default:
            break;
    }
}

这里只举例了 4 种积分类型,实际情况可能并不止这么多,而且每种积分发放逻辑都不相同,如果一味的在 switch case 中堆代码是肯定不行,根据我们的设计原则,尽量保证高内聚、低耦合,很显然这种方式是不可取的。

使用策略模式优化下上面的代码,首先我们需要一个策略接口,即积分发放接口:

java 复制代码
/**
 * 积分发放接口,各个积分类型需要实现该接口,自定义发放逻辑
 */
public interface PointSendInterface {
    /**
     * 发放积分
     * @param points 要发放的积分
     */
    void sendPoint(Integer points);

    /**
     * 积分类型
     * @return
     */
    PointTypeEnum sendType();
}

然后不同的积分类型需要实现该接口,以便自定义其积分发放规则:

java 复制代码
@Slf4j
@Service
public class CourseCommentPointSend implements PointSendInterface {
    @Override
    public void sendPoint(Integer points) {
        log.info("评论课程,获得 {} 积分", points);
    }

    @Override
    public PointTypeEnum sendType() {
        return PointTypeEnum.COURSE_COMMENT;
    }
}

@Slf4j
@Service
public class CourseLearnPointSend implements PointSendInterface {
    @Override
    public void sendPoint(Integer points) {
        log.info("学习课程,获得 {} 积分", points);
    }

    @Override
    public PointTypeEnum sendType() {
        return PointTypeEnum.COURSE_LEARN;
    }
}

@Slf4j
@Service
public class EbookReadPointSend implements PointSendInterface {
    @Override
    public void sendPoint(Integer points) {
        log.info("阅读电子书,获得 {} 积分", points);
    }

    @Override
    public PointTypeEnum sendType() {
        return PointTypeEnum.EBOOK_READ;
    }
}

@Slf4j
@Service
public class LiveWatchPointSend implements PointSendInterface {
    @Override
    public void sendPoint(Integer points) {
        log.info("观看直播,获得 {} 积分", points);
    }

    @Override
    public PointTypeEnum sendType() {
        return PointTypeEnum.LIVE_WATCH;
    }
}

最后我们要实现一个积分发放的 Context 给业务方使用,大致如下:

java 复制代码
/**
 * 积分发放类
 */
@Component
public class PointSendContext {
    @Autowired
    private List<PointSendInterface> sendStrategyList;

    private final Map<PointTypeEnum, PointSendInterface> SEND_STRATEGY_MAP = new HashMap<>();

    @PostConstruct
    public void init() {
        sendStrategyList.forEach(strategy -> {
            SEND_STRATEGY_MAP.put(strategy.sendType(), strategy);
        });
    }

    /**
     * 发放积分
     * @param pointTypeEnum 积分类型
     * @param points 要发的分
     */
    public void sendPoint(PointTypeEnum pointTypeEnum, Integer points) {
        PointSendInterface sendStrategy = SEND_STRATEGY_MAP.get(pointTypeEnum);
        // 调用具体实现类发放积分
        sendStrategy.sendPoint(points);
    }
}

这里我们通过 @Autowired 自动注入 PointSendInterface 接口的所有实现类 sendStrategyList,然后通过 @PostConstruct 的初始化方法将每一个实现类组装成 SEND_STRATEGY_MAP 对象以便后续直接通过 pointType 可以映射出来该 pointType 对应的发分方法。调用的话就比较简单了,没有了 switch case 的冗长:

java 复制代码
@SpringBootTest
class PointSendContextTest {
    @Autowired
    private PointSendContext pointSendContext;

    @Test
    void sendPoint() {
        pointSendContext.sendPoint(PointTypeEnum.COURSE_LEARN, 20);
    }
}
相关推荐
wangchunyu1145 小时前
JAVA CompletableFuture异步编程实战
java·开发语言·python
paopaokaka_luck6 小时前
流浪动物救助与收养系统(DeepSeek AI对话、 ECharts多维分析、救助流程衔接、领养与回访衔接、 捐赠确认与公示、志愿者协作)
java·前端·javascript·人工智能·mysql·echarts·推荐算法
Thomas.Sir6 小时前
第9课:SpringBoot4.x Web开发基础:WebMvc自动配置、静态资源、请求参数接收
java·spring boot
程序 代码狂人7 小时前
切片表与分区表的区别(数据开发面试版)
java·大数据·开发语言·数据仓库·算法
2501_944521597 小时前
Deepmerge 跨端三方库仓颉适配全流程
java·python·eclipse·github
南归北隐9 小时前
Spring AI Alibaba Graph框架实现MCP数据库查询实战
java·后端·spring ai·mcp·spring ai mcp
古法老登瞎折腾9 小时前
大数据智能运维实战(九):让 AI 查日志——找到异常,也保留上下文
java·大数据·运维·人工智能·spring ai·mcp
ai大模型-探索者9 小时前
Function Call与MCP(区别、联系与实战)
java·服务器·网络
略略略咯咯9 小时前
啊啊啊啊啊
java·服务器·数据库
SL_staff9 小时前
运维救火实战:钉钉API变更致薪资计算中断,如何用可视化逻辑引擎5分钟热修复
java·开源·全栈