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

策略模式简介

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

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

包含的角色如下:

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

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

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

/**
 * 积分类型枚举
 */
public enum PointTypeEnum {
    COURSE_LEARN,
    COURSE_COMMENT,
    EBOOK_READ,
    LIVE_WATCH,
}

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

/**
 * 指定积分类型,发分
 * @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 中堆代码是肯定不行,根据我们的设计原则,尽量保证高内聚、低耦合,很显然这种方式是不可取的。

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

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

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

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

@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 给业务方使用,大致如下:

/**
 * 积分发放类
 */
@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 的冗长:

@SpringBootTest
class PointSendContextTest {
    @Autowired
    private PointSendContext pointSendContext;

    @Test
    void sendPoint() {
        pointSendContext.sendPoint(PointTypeEnum.COURSE_LEARN, 20);
    }
}
posted @ 2023-09-19 22:20  程序员小波与Bug  阅读(47)  评论(0编辑  收藏  举报