一.场景需求
假设公司现在有一个营销活动,需要用户参加,完成一系列的任务,最后可以得到一些奖励作为回报.
现在需要你设计并实现任务模型.需要重点关注状态的流转变更和状态变更后的通知.
二.前期分析,代码编写
既然是一个任务,必然是有它的状态的,比如说初始化,完成,过期等等.我们可以为任务状态定义一个枚举类.
那每个人去做自己的任务,也必然有自己当前所属的任务状态,换句话说就是用户的不同行为会触发不同的任务状态.所以我们对用户的行为状态也定义一个枚举类.
然后我们这里主要编写状态变更功能.
代码如下:
定义的两个枚举类
@AllArgsConstructor
@Getter
enum TaskStatus{
INIT("初始化"),
ONGOING("进行中"),
PAUSED("暂停中"),
FINISHED("已完成"),
EXPIRED("已过期");
private final String message;
}
@AllArgsConstructor
@Getter
enum ActionType{
START(1,"开始"),
STOP(2,"暂停"),
ACHIEVE(3,"完成"),
EXPIRED(4,"过期");
private final int code;
private final String message;
}
状态变更功能
public class Task {
//任务id
private Long taskId;
//任务的默认状态均为初始化
private TaskStatus taskStatus=TaskStatus.INIT;
//活动依赖的外部服务
private ActivityService activityService;
//任务管理器
private TaskManager taskManager;
//使用条件分支进行任务更新
public void updateStatus(ActionType actionType){
//任务状态初始化
if(taskStatus == TaskStatus.INIT ){
if(actionType == ActionType.START){
taskStatus = TaskStatus.ONGOING;
}
//任务进行中
}else if(taskStatus == TaskStatus.ONGOING){
if(actionType == ActionType.ACHIEVE){
taskStatus = TaskStatus.FINISHED;
//任务完成后通知外部服务,可以发放奖励了
activityService.notifyFinish();
taskManager.release(taskId);
}else if(actionType == ActionType.STOP){
taskStatus = TaskStatus.PAUSED;
}else if(actionType == ActionType.EXPIRED){
taskStatus = TaskStatus.EXPIRED;
}
//任务暂停
}else if(taskStatus == TaskStatus.PAUSED){
if(actionType == ActionType.START){
taskStatus = TaskStatus.ONGOING;
}else if(actionType == ActionType.EXPIRED){
taskStatus = TaskStatus.EXPIRED;
}
}
}
}
在上述的实现中,主要有两个重要的功能
- 接收不同的行为,然后更新当前任务的状态
- 当任务完成后,通知任务所属的活动和任务管理器
诚然,上述代码实现了最基本的功能,但是还存在很多问题.
- 不够优雅,上述代码使用很多条件语句,很臃肿,且不具备扩展性,维护难度也大,当有新增状态时,还需要新增if-else语句,违背了开闭原则.
- 任务类不够高内聚, 它在通知实现中感知了其他领域或模块的具体模型,如具体的外部服务和任务管理器,这样代码的耦合度太高,不利于扩展
那怎样进行优化呢?
- 这两个问题其实都可以通过设计模式去进行优化,首先状态的流转控制可以使用状态模式,其次,任务完成后的通知可以使用 观察者模式
三.使用设计模式进行优化
1.状态模式
根据状态模式的定义:
我们把TaskState枚举类扩展成多个状态类,并具备完成状态的流转的能力
//任务状态抽象接口
public interface TaskStatus {
//默认空实现,供子类调用或重写
default void update(Task task,ActionType actionType) {
//do nothing
}
}
//任务初始状态
class TaskInit implements TaskStatus {
@Override
public void update(Task task, ActionType actionType) {
if (actionType == ActionType.START) {
task.setTaskStatus(new TaskOngoing());
}
}
}
//任务进行状态
class TaskOngoing implements TaskStatus {
private ActivityService activityService;
private TaskManager taskManager;
private Long taskId;
@Override
public void update(Task task, ActionType actionType) {
if (actionType == ActionType.ACHIEVE) {
task.setTaskStatus(new TaskFinished());
// 通知
activityService.notifyFinish(taskId);
taskManager.release(taskId);
} else if (actionType == ActionType.STOP) {
task.setTaskStatus(new TaskPaused());
} else if (actionType == ActionType.EXPIRED) {
task.setTaskStatus(new TaskExpired());
}
}
}
//任务暂停状态
class TaskPaused implements TaskStatus {
@Override
public void update(Task task, ActionType actionType) {
if (actionType == ActionType.START) {
task.setTaskStatus(new TaskOngoing());
} else if (actionType == ActionType.EXPIRED) {
task.setTaskStatus(new TaskExpired());
}
}
}
// 任务完成状态
class TaskFinished implements TaskStatus {
}
// 任务过期状态
class TaskExpired implements TaskStatus {
}
@Data
public class Task {
public Task(Long taskId, TaskStatus taskStatus) {
this.taskId = taskId;
this.taskStatus = taskStatus;
}
private Long taskId;
//任务默认为初始状态
private TaskStatus taskStatus=new TaskInit();
public void update(ActionType actionType){
taskStatus.update(this,actionType);
}
//测试调用
public static void main(String[] args) {
Task task = new Task(2343L, new TaskOngoing());
task.update(ActionType.ACHIEVE);
}
}
@AllArgsConstructor
@Getter
enum ActionType{
START(1,"开始"),
STOP(2,"暂停"),
ACHIEVE(3,"完成"),
EXPIRED(4,"过期");
private final int code;
private final String message;
}
可以看到,经过状态模式处理后的任务类的耦合度得到降低,符合开闭原则,状态模式的优点在于符合单一职责原则,状态类职责明确,有利于程序的扩展,但是这样设计的代价是状态类的数目增加了,因为状态流逻辑越复杂,需要处理的动作越多,越有利于状态模式的应用.除此之外,状态类的自身对于开闭原则的支持没有足够好,如果状态流转逻辑变化频繁,那么可能要慎重使用.
现在,就再根据观察者模式去优化任务完成时的通知:
2.观察者模式.
在我们使用观察者模式的时候,被通知的,需要做出行动的对象是观察者.产生消息,发布通知的就是主题
所以在这个场景中,被通知的任务管理器,外部服务就是具体的观察者,任务状态变成结束时需要去发布通知,所以任务状态就是具体的主题.
先设计好抽象的观察者和具体的观察者
//抽象观察者
public interface Observer {
void response(Long taskId);
}
//外部服务观察者
class ActivityServiceObserver implements Observer {
private ActivityService activityService;
@Override
public void response(Long taskId) {
activityService.notifyFinish(taskId);
}
};
//任务管理观察者
class TaskManagerObserver implements Observer {
private TaskManager taskManager;
@Override
public void response(Long taskId) {
taskManager.release(taskId);
}
};
定义好抽象的主题和具体的主题,这里是将任务进行状态作为一个具体的主题.
同时在任务初始状态流转时,定义好所需的观察者,并且将任务进行状态中的通知优化成通用的通知方法.
//抽象主题
abstract class Subject {
private List<Observer> observers = new ArrayList<>();
//增加观察者
public void addObserver(Observer observer){
observers.add(observer);
}
//删除观察者
public void deleteObserver(Observer observer){
observers.remove(observer);
}
//通知观察者
public void notifyAll(Long taskId){
observers.forEach(observer -> observer.response(taskId));
}
}
/任务初始状态
class TaskInit implements TaskStatus {
@Override
public void update(Task task, ActionType actionType) {
if (actionType == ActionType.START) {
TaskOngoing taskOngoing = new TaskOngoing();
task.setTaskStatus(taskOngoing);
//在任务初始化的时候,将需要的通知的观察者放到集合中
taskOngoing.addObserver(new ActivityServiceObserver());
taskOngoing.addObserver(new TaskManagerObserver());
}
}
}
//任务进行状态,也是具体的主题
class TaskOngoing extends Subject implements TaskStatus {
private ActivityService activityService;
private TaskManager taskManager;
private Long taskId;
@Override
public void update(Task task, ActionType actionType) {
if (actionType == ActionType.ACHIEVE) {
task.setTaskStatus(new TaskFinished());
//利用观察者模式进行通知
notifyAll(taskId);
} else if (actionType == ActionType.STOP) {
task.setTaskStatus(new TaskPaused());
} else if (actionType == ActionType.EXPIRED) {
task.setTaskStatus(new TaskExpired());
}
}
}
通过观察者模式,让任务状态和通知方实现松耦合(实际上观察者模式还没能做到完全的解耦,如果要做进一步的解耦可以考虑学习并使用发布-订阅模式,这里也不再赘述)。
至此,我们成功使用状态模式设计出了高内聚、高扩展性、单一职责的任务的整个状态机实现,以及做到松耦合的、符合依赖倒置原则的任务状态变更通知方式。
其实,设计模式的唯一一点就是找到变化,封装变化