如何使用策略模式处理多种类型请求数据_全局页面替换策略

如何使用策略模式处理多种类型请求数据_全局页面替换策略现在有一个活动,活动场景包含布置书籍作业,布置短文作业,布置一课一练作业(以后还可能会新增其它类型的活动),每一种活动场景有自己对应的完成逻辑和奖励。现在定义对应的场景值如下: 这种方式是最简单的,也是最容易理解的,但是存在的问题是,如果现在新增新的活动场景,原来的if els…

1.需求简述

现在有一个活动,活动场景包含布置书籍作业,布置短文作业,布置一课一练作业(以后还可能会新增其它类型的活动),每一种活动场景有自己对应的完成逻辑和奖励。现在定义对应的场景值如下:

活动名称 活动场景值
布置书籍作业 11
布置短文作业 12
布置一课一练作业 13

2.解决方案

解决方案一:因为布置作业和完成活动属于不同的项目,我采用的是消息队列的方式(消息队列不是本文讨论的重点),布置作业时发送消息,传递对应的活动场景值和其它必须的参数过来,消费端收到消息之后,根据对应的场景值作出相应的处理,伪代码如下:

if(场景值==11){
    //完成书籍作业的相关逻辑
}else if(场景值==12){
    //完成短文作业的相关逻辑
}else if(场景值==13){
    //完成一课一练作业的相关逻辑
}

这种方式是最简单的,也是最容易理解的,但是存在的问题是,如果现在新增新的活动场景,原来的if else后面要新增新的代码和判断逻辑,对原有的代码具有侵入性;再者如果类型非常多的话,if else也会有很多,代码看起来不够优雅。 解决方案二:采用策略模式来解决,定义一个策略接口,布置书籍作业,短文作业,一课一练的处理逻辑都实现策略接口,根据传入的不同场景值选择不同的处理类。这也是使用策略模式最难的地方:如何根据传入的参数,找到对应的处理类,答案是:可以采用spring的getBean,或者是java的反射。我在程序启动时就加载所有的策略类到内存中,处理请求时,根据传入的参数,选择对应的处理类。

3.实操代码

3.1定义策略接口

/** * 活动策略接口 * @author junzhongliu * @date 2018/9/30 17:11 */
public interface ActivityStrategyInterface {

    /** * 教师创建或更新活动记录 * @param userId 用户id * @param scene 场景 * @param condition 本活动完成的条件 */
    void doActivityAction(Long userId,Integer scene,Integer condition);
}

3.2自定义注解

为了方便比对传入的场景值,选择对应的策略处理类,我自定义了一个注解

/** * 活动场景注解 * @author junzhongliu * @date 2018/9/30 17:24 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivitySceneAnnotation {

    /** * 活动场景id,默认值1 */
    int sceneId() default 1;
}

3.3定义对应的策略处理接口

就是真正处理布置书籍作业,短文作业,一课一练作业的策略实现类,它们是要实现策略接口的,代码如下:

/** * 布置书籍任务 * @author junzhongliu * @date 2018/9/30 17:13 */
@Slf4j
@Service
@ActivitySceneAnnotation(sceneId = ActivitySceneConstants.BOOK_ACTIVITY)
public class BookStrategy implements ActivityStrategyInterface {

    @Autowired
    TeacherActivityRecordService teacherActivityRecordService;

    @Override
    public void doActivityAction(Long userId, Integer scene, Integer condition) {
        log.info("desc:{},userId:{},scene:{},condition:{}","布置书籍任务[STRATEGY]",userId,scene,condition);
        teacherActivityRecordService.saveOrUpdateRookieActivityRecord(userId,scene,condition);
    }
}

这里的ActivitySceneAnnotation就是上面我们自定义的注解,ActivitySceneConstants.BOOK_ACTIVITY是自定义的常量,真实值是11,对应上面的场景值,可以看到在处理的过程中是调用了service来处理的,其它的短文任务,一课一练任务跟这个基本一样的,只是不同的场景值,只在展示一个布置短文作业的:


/** * 完成短文活动任务 * @author junzhongliu * @date 2018/9/30 17:13 */
@Slf4j
@Service
@ActivitySceneAnnotation(sceneId = ActivitySceneConstants.PASSAGE_ACTIVITY)
public class PassageStrategy implements ActivityStrategyInterface {

    @Resource
    TeacherActivityRecordService teacherActivityRecordService;

    @Override
    public void doActivityAction(Long userId, Integer scene, Integer condition) {
        log.info("desc:{},userId:{},scene:{},condition:{}","布置短文任务[STRATEGY]",userId,scene,condition);
        teacherActivityRecordService.saveOrUpdateRookieActivityRecord(userId,scene,condition);
    }
}

3.4根据场景值选择对应的处理类

我这里是在程序启动时,采用@PostConstruct注解,将实现ActivityStrategyInterface接口的所有策略类都加载到内存中了,用户请求传过来一个场景值,根据这个场景值,选择对应的处理类,全部代码如下:

/** * 策略处理类的工厂类 * @author junzhongliu * @date 2018/9/30 17:17 */
@Service
public class ActivityStrategyFactory {

    private static final Map<String,ActivityStrategyInterface> STRATEGY_BEAN_CACHE = Maps.newConcurrentMap();
    @Autowired
    private ApplicationContext applicationContext;

    /** * 根据不同的场景创建不同的策略 * 实现思路:遍历策略列表的所有策略,获取策略的注解, * 比对场景值是否一致,场景值一致则返回当前策略的实例对象 * @param scene 场景值 * @return */
    public ActivityStrategyInterface createStrategy(Integer scene) {

        Optional<ActivityStrategyInterface> strategyOptional =
                STRATEGY_BEAN_CACHE
                        .entrySet()
                        .stream()
                        .map(e -> {
            ActivitySceneAnnotation validScene = e.getValue().getClass().getDeclaredAnnotation(ActivitySceneAnnotation.class);
            if (Objects.equals(validScene.sceneId(),scene)) {
                    return e.getValue();
            }
            return null;
        }).filter(Objects::nonNull)
                        .findFirst();
        if(strategyOptional.isPresent()){
            return strategyOptional.get();
        }
        throw new RuntimeException("策略获得失败");
    }

    /** * 初始化策略列表 */
    @PostConstruct
    private void init() {
        STRATEGY_BEAN_CACHE.putAll(applicationContext.getBeansOfType(ActivityStrategyInterface.class));
    }

}

到现在为止,策略相关的处理已经定义完了,接下来看如何使用

3.5如何使用

我是在消息的消费处调用了ActivityStrategyFactory,传入场景值,获取处理类,代码如下:

/** * 消费其它模块的消息,创建或更新教师活动记录 * @author junzhongliu * @date 2018/9/30 16:50 */
@Slf4j
@Service
public class CreateActivityRecordMessageConsumer implements MessageConsumer<CreateActivityRecordMessage> {

    @Autowired
    private ActivityStrategyFactory strategyFactory;

    @Override
    public CreateActivityRecordMessage newMessageInstance() {
        return new CreateActivityRecordMessage();
    }

    @Override
    public void consume(CreateActivityRecordMessage message) throws Exception {
        log.info("desc:{},param:{}","创建任务记录消费消息[CONSUMER]",JSONObject.toJSONString(message));
        Long userId = message.getUserId();
        Integer scene = message.getScene();
        Integer condition = message.getCondition();
        if(Objects.isNull(userId) || Objects.isNull(scene) || Objects.isNull(condition)){
            return;
        }
        //创建具体的执行策略,并执行活动行为
        ActivityStrategyInterface strategy = strategyFactory.createStrategy(message.getScene());
        strategy.doActivityAction(userId,scene,condition);
    }

}

这是整个过程的全部代码,如果现在新增其它活动场景(比如布置假期作业),那么直接写一个布置假期作业的处理类,新增一个对应的场景值就可以了,对原有代码不侵入。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/13671.html

(0)

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注