1. Краткое описание требований
Теперь есть активность. Сцена активности включает в себя задание книжных заданий, назначение заданий для эссе, назначение одного урока и одного практического задания (другие типы действий могут быть добавлены в будущем). Каждая сцена действия имеет свою собственную соответствующую логику завершения и награды. Теперь определите соответствующие значения сцены следующим образом:
| Название события | Значение активной сцены |
|---|---|
| Назначать задания книги | 11 |
| Назначать задания для эссе | 12 |
| Назначить один урок и одно практическое домашнее задание | 13 |
2. Решения
Решение а. Поскольку задания и действия по завершению относятся к разным элементам, я использую очереди режима сообщений (фокус очереди сообщений здесь не обсуждается), передает назначения сообщений, ретранслирует сцену, соответствующую значению, и другие необходимые параметры. После этого, потребитель получает сообщение о завершении, выполняет соответствующую обработку в соответствии со значением, соответствующим сцене, следующим псевдокодом:
if(场景值==11){
//完成书籍作业的相关逻辑
}else if(场景值==12){
//完成短文作业的相关逻辑
}else if(场景值==13){
//完成一课一练作业的相关逻辑
}
Этот метод является самым простым и легким для понимания, но проблема в том, что если сейчас добавляется новая сцена активности, новый код и логика суждения должны быть добавлены после исходного if else, что не влияет на исходный код. Кроме того, если типов слишком много, будет много if elses, и код не будет выглядеть элегантно. Решение 2. Используйте режим стратегии для решения проблемы, определите интерфейс стратегии, упорядочите задания для книг, задания для эссе и логику обработки одного урока и одной практики — все реализуют интерфейс стратегии и выбирают разные классы обработки в соответствии с различными поступающими сообщениями. значения сцены. Это также самая сложная часть использования шаблона стратегии: как найти соответствующий класс обработки в соответствии с входящими параметрами, ответ: вы можете использовать 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, что соответствует указанному выше значению сцены Вы можете видеть, что служба вызывается во время обработки Другие короткие тексты Задачи, один урок , одно практическое задание в основном такое же, как это, но с другими значениями сцены, показывает только короткое задание эссе:
/**
* 完成短文活动任务
* @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);
}
}
Это весь код всего процесса.Если вы добавите другие сценарии деятельности (такие как организация отпускных заданий), вы можете напрямую написать класс обработки для назначения отпускных заданий и добавить соответствующее значение сцены, не вторгаясь в исходный код.