观察者模式、状态模式在实际工作中的使用

一.场景需求

      假设公司现在有一个营销活动,需要用户参加,完成一系列的任务,最后可以得到一些奖励作为回报.
      现在需要你设计并实现任务模型.需要重点关注状态的流转变更和状态变更后的通知.

二.前期分析,代码编写

      既然是一个任务,必然是有它的状态的,比如说初始化,完成,过期等等.我们可以为任务状态定义一个枚举类.
      那每个人去做自己的任务,也必然有自己当前所属的任务状态,换句话说就是用户的不同行为会触发不同的任务状态.所以我们对用户的行为状态也定义一个枚举类.
然后我们这里主要编写状态变更功能.

代码如下:

定义的两个枚举类

@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;
            }
        }


    }




}

在上述的实现中,主要有两个重要的功能

  1. 接收不同的行为,然后更新当前任务的状态
  2. 当任务完成后,通知任务所属的活动和任务管理器

诚然,上述代码实现了最基本的功能,但是还存在很多问题.

  1. 不够优雅,上述代码使用很多条件语句,很臃肿,且不具备扩展性,维护难度也大,当有新增状态时,还需要新增if-else语句,违背了开闭原则.
  2. 任务类不够高内聚, 它在通知实现中感知了其他领域或模块的具体模型,如具体的外部服务和任务管理器,这样代码的耦合度太高,不利于扩展

那怎样进行优化呢?

  • 这两个问题其实都可以通过设计模式去进行优化,首先状态的流转控制可以使用状态模式,其次,任务完成后的通知可以使用 观察者模式

三.使用设计模式进行优化

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());
        }
    }
}

      通过观察者模式,让任务状态和通知方实现松耦合(实际上观察者模式还没能做到完全的解耦,如果要做进一步的解耦可以考虑学习并使用发布-订阅模式,这里也不再赘述)。
      至此,我们成功使用状态模式设计出了高内聚、高扩展性、单一职责的任务的整个状态机实现,以及做到松耦合的、符合依赖倒置原则的任务状态变更通知方式。
      其实,设计模式的唯一一点就是找到变化,封装变化

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员bling

义父,感谢支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值