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


    一.场景需求

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

    二.前期分析,代码编写

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

    代码如下:

    定义的两个枚举类

    @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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    状态变更功能

    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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

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

    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 {
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    @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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

          可以看到,经过状态模式处理后的任务类的耦合度得到降低,符合开闭原则,状态模式的优点在于符合单一职责原则,状态类职责明确,有利于程序的扩展,但是这样设计的代价是状态类的数目增加了,因为状态流逻辑越复杂,需要处理的动作越多,越有利于状态模式的应用.除此之外,状态类的自身对于开闭原则的支持没有足够好,如果状态流转逻辑变化频繁,那么可能要慎重使用.

    现在,就再根据观察者模式去优化任务完成时的通知:

    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);
        }
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    定义好抽象的主题和具体的主题,这里是将任务进行状态作为一个具体的主题.
    同时在任务初始状态流转时,定义好所需的观察者,并且将任务进行状态中的通知优化成通用的通知方法.

    
    //抽象主题
    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));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    /任务初始状态
    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());
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

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

    在这里插入图片描述

  • 相关阅读:
    Mac那些好用的软件(持续更新)
    wordpress模板购买网站推荐
    剑指 Offer II 105. 岛屿的最大面积
    20221115使用google文档翻译SRT格式的字幕
    第六届“中国法研杯”司法人工智能挑战赛进行中!
    Activiti7 教程心得【1】
    为啥外行都觉得程序员的代码不值钱?
    【模板】2-SAT
    单例模式坑这么多,不会用别乱用
    k8s-项目测试环境部署
  • 原文地址:https://blog.csdn.net/wangliangluang/article/details/125470211