任务模型的设计
在 任务模型的设计 这段,我觉得 TaskState 应该是一个不可变的值对象,它不应该有过多的创建 new,枚举+不同实现的方法我觉得是个不错的选择。另外,TaskState 应该独立于 Task 对象,它的方法参数中不依赖 Task 参数。
这是我改进的 TaskState 类:
/**
* 任务状态枚举
*/
@AllArgsConstructor
@Getter
public enum TaskState {
INIT("初始化", TaskState::dispatchOnInit),
ONGOING("进行中", TaskState::dispatchOnOngoing),
PAUSED("暂停中", TaskState::dispatchOnPaused),
FINISHED("已完成", TaskState::dispatchOnFinished),
EXPIRED("已过期", TaskState::dispatchOnExpired);
private final String message;
private final Function<ActionType, TaskState> reducer;
public TaskState dispatch(ActionType actionType) {
return reducer.apply(actionType);
}
// 这里直接使用方法引用的方式在class里直接实现逻辑,当然也可以包装到新 Function 实现类里
private static TaskState dispatchOnInit(ActionType actionType) {
switch (actionType) {
case START:
return ONGOING;
default:
return INIT;
}
}
private static TaskState dispatchOnOngoing(ActionType actionType) {
switch (actionType) {
case ACHIEVE:
return FINISHED;
case STOP:
return PAUSED;
case EXPIRE:
return EXPIRED;
default:
return ONGOING;
}
}
private static TaskState dispatchOnPaused(ActionType actionType) {
switch (actionType) {
case START:
return ONGOING;
case EXPIRE:
return EXPIRED;
default:
return PAUSED;
}
}
private static TaskState dispatchOnFinished(ActionType actionType) {
return FINISHED;
}
private static TaskState dispatchOnExpired(ActionType actionType) {
return EXPIRED;
}
}
/**
* 行为枚举
*/
@AllArgsConstructor
@Getter
enum ActionType {
START(1, "开始"),
STOP(2, "暂停"),
ACHIEVE(3, "完成"),
EXPIRE(4, "过期");
private final int code;
private final String message;
}
- 每个
TaskState实例有自己接受不同actionType时的行为,即reducer字段 dispatchOn*方法为TaskState接受不同actionType时的行为具体实现TaskState不可变,每次dispatch都返回新的TaskStatedispatchOn*可以定义为方法来编写,也可以独立出来作为Function实现类来编写
使用发布-订阅模式来代替发布-订阅模式进行解耦,这里使用 Vert.X 的 EventBus 作为发布订阅管道:
public class Task {
private final Long taskId;
private TaskState state = TaskState.INIT;
private final EventBus eventBus;
public void updateState(ActionType actionType) {
TaskState startState = state;
state = state.dispatch(actionType);
// 发布事件
// 1. 当状态变为 FINISHED 时发送事件
if ( startState != TaskState.FINISHED && state == TaskState.FINISHED) {
eventBus.publish(TaskState.FINISHED.name(), taskId);
}
}
}
订阅事件,调用活动服务和任务管理器
eventBus.<Long>consumer(TaskState.FINISHED.name(), msg -> {
Long taskId = msg.body();
// 模拟调用活动服务
System.out.println("活动服务: " + taskId + " 完成");
// 模拟调用任务管理器
System.out.println("任务管理器: " + taskId + " 完成");
});
写段测试代码,测试下是否可用
public static void main(String[] args) {
EventBus eventBus = Vertx.vertx().eventBus();
// 订阅事件
eventBus.<Long>consumer(TaskState.FINISHED.name(), msg -> {
Long taskId = msg.body();
// 模拟调用活动服务
System.out.println("活动服务: " + taskId + " 完成");
// 模拟调用任务管理器
System.out.println("任务管理器: " + taskId + " 完成");
});
// 测试
Task task = new Task(1002L, eventBus);
print(task, ActionType.START);
print(task, ActionType.STOP);
print(task, ActionType.START);
print(task, ActionType.ACHIEVE);
print(task, ActionType.EXPIRE);
task = new Task(1003L, eventBus);
print(task, ActionType.STOP);
print(task, ActionType.START);
print(task, ActionType.EXPIRE);
print(task, ActionType.START);
print(task, ActionType.ACHIEVE);
}
static void print(Task task, ActionType actionType) {
TaskState startState = task.getState();
task.updateState(actionType);
TaskState endState = task.getState();
System.out.printf("Task[%s]: %s + %s -> %s \n", task.getTaskId(), startState, actionType, endState );
}
控制台输出:
因为订阅服务的执行是异步的,所以控制台输出顺序不是串行的
可见,任务的状态变化和订阅服务的执行是符合预期的。