设计模式之观察者模式详解
·
观察者模式
一、什么是观察者模式
观察者模式 (Observer Pattern) 又叫发布-订阅模式 (Publish/Subscribe),定义一种一对多的依赖关系,一个主题对象 (被观察对象) 可被多个观察者对象同时监听,使得每当主题对象状态变化时,所有依赖于它的对象都会得到通知并被更新,属于行为型模式。
观察者模式的核心是将观察者与主题对象解耦,以类似于消息、广播发送的机制联动两者,使被观察者的变动能通知到感兴趣的观察者们,从而做出相应的响应。
二、观察者模式包含的角色
观察者模式的通用类图:
抽象主题 (Subject):指被观察的对象 (Observable)。该角色是一个抽象类或接口,定义了增加、删除、通知观察者对象的方法
具体主题 (Concrete Subject):具体被观察者,当其内部状态变化时,会通知已注册的观察者
抽象观察者 (Observer):定义了响应通知的更新方法
具体观察者 (Concrete Observer):在得到状态更新时,会自动做出响应
三、观察者模式通用写法
抽象主题(Subject)的内部变化会通知到感兴趣的观察者,观察者会根据通知做出相应处理
- 创建抽象主题角色
ISubject
/** * @ClassName: ISubject * @description: 抽象主题角色,被观察者对象 * @author: zdp * @create: 2022-09-10 15:20 **/ public interface ISubject<T> { /** * 注册观察者 * * @Author zdp * @Date 2022/9/10 15:35 * @param observer observer * @return 新增结果 */ boolean addObserver(IObserver<T> observer); /** * 移除观察者 * * @Author zdp * @Date 2022/9/10 15:35 * @param observer observer * @return 移除结果 */ boolean remove(IObserver<T> observer); /** * 被观察者内部状态发生变动通知观察者的方法 * @Author zdp * @Date 2022/9/10 15:35 */ void notify(T event); }
- 创建具体主题角色
ConcreteSubject
/** * @ClassName: ConcreteSubject * @description: 具体主题角色,具体被观察对象 * @author: zdp * @create: 2022-09-10 15:21 **/ public class ConcreteSubject<T> implements ISubject<T> { /** * 观察者注册容器 */ List<IObserver<T>> observers = new LinkedList<>(); @Override public boolean addObserver(IObserver<T> observer) { if (observers.contains(observer)) { return true; } return observers.add(observer); } @Override public boolean remove(IObserver<T> observer) { if (observers.contains(observer)) { return observers.remove(observer); } return true; } @Override public void notify(T event) { if (!observers.isEmpty()) { for (IObserver<T> observer : observers) { observer.update(event); } } else { System.err.println("当前没有观察者..."); } } }
- 创建抽象观察者
Observer
/** * @ClassName: IObserver * @description: 抽象观察者 * @author: zdp * @create: 2022-09-10 15:21 **/ public interface IObserver<T> { /** * 观察者接收通知的业务处理方法 * * @Author zdp * @Date 2022/9/10 15:44 * @param event 被观察者状态变动发送的通知参数 */ void update(T event); }
- 创建具体观察者
ConcreteObserver
/** * @ClassName: ConcreteObserver * @description: 具体观察者角色 * @author: zdp * @create: 2022-09-10 15:22 **/ public class ConcreteObserver<T> implements IObserver<T> { @Override public void update(T event) { System.out.println("具体观察者 ConcreteObserver 接收到消息: " + event.toString()); } }
- 通用写法验证
/** * @ClassName: Test * @description: 观察者模式通用写法测试 * @author: zdp * @create: 2022-09-10 15:24 **/ public class Test { public static void main(String[] args) throws InterruptedException { //主题对象(被观察者对象) ISubject<String> subject = new ConcreteSubject<>(); //抽象观察者 IObserver<String> observer = new ConcreteObserver<>(); //注册观察者 subject.addObserver(observer); //主题对象变动向观察者推送消息 subject.notify("消息过来了...."); //移除观察者 subject.remove(observer); //再次推送 subject.notify("消息过来了...."); } }
四、观察者模式在业务中的使用举例
在问答系统中学生可以向老师提问,当设置了指定老师回答,对应老师就能够收到相应的通知,在现有的api中,jdk和guava将观察者模式分别为我们封装了现成的api,我们直接使用即可
- 使用
Jdk
为我们封装的方法-
抽象主题对象是
Jdk
提供的Observable
-
创建主题对象(被观察者对象)
Student
/** * @ClassName: Student * @description: 具体主题角色,学生对象 * @author: zdp * @create: 2022-09-10 16:35 **/ @Data @NoArgsConstructor @AllArgsConstructor public class Student extends Observable { private String studentName; public void pushQuestion(Question question){ System.out.println("学生【" + this.studentName + "】在系统中推送了一个问题,请您及时查看~"); //设置主题对象内部状态发生变动 setChanged(); //通知观察者 (老师) notifyObservers(question); } } /** * @ClassName: Question * @description: 通知传递参数 * @author: zdp * @create: 2022-09-10 16:42 **/ @Data @NoArgsConstructor @AllArgsConstructor class Question { private String content; private String studentName; }
-
抽象观察者是
Jdk
提供的Observer
-
创建具体观察者对象
Teacher
/** * @ClassName: Teacher * @description: 观察者对象 * @author: zdp * @create: 2022-09-10 16:43 **/ @Data @AllArgsConstructor public class Teacher implements Observer { private String teacherName; /** * @param o 具体的被观察对象 * @param arg 通知参数 */ @Override public void update(Observable o, Object arg) { Student student = (Student) o; Question question = (Question) arg; System.out.println(this.teacherName + " 您好~ 您收到来自学生【" + student.getStudentName()+ "】的提问! 问题是:" + question.getContent() + " 请您及时解答~"); } } ```
-
测试
public class Test { public static void main(String[] args) { //创建具体的被观察者对象 Student student = new Student("zdp"); //注册观察者 Teacher one = new Teacher("Teacher One.."); Teacher two = new Teacher("Teacher Two.."); student.addObserver(one); student.addObserver(two); //推送通知 Question question = new Question("什么是观察者模式? ", student.getStudentName()); student.pushQuestion(question); } }
-
2. 使用Guava为我们封装的api
- 创建观察者
Teacher
/** * @ClassName: Teacher * @description: 观察者对象 * @author: zdp * @create: 2022-09-10 16:43 **/ @Data @AllArgsConstructor public class Teacher { private String teacherName; @Subscribe public void update(Student stu) { System.out.println(this.teacherName + " 您好~ 您收到来自学生【" + stu.getStudentName()+ "】的提问! 问题是:" + stu.getQuestion().getContent() + " 请您及时解答~"); } }
- 创建通知参数
/** * @ClassName: Student * @description: 具体主题角色,学生对象 * @author: zdp * @create: 2022-09-10 16:35 **/ @Data @NoArgsConstructor @AllArgsConstructor public class Student { private String studentName; private Question question; /** * @ClassName: Question * @description: 通知参数 * @author: zdp * @create: 2022-09-10 16:42 **/ @Data @NoArgsConstructor @AllArgsConstructor static class Question { private String content; } }
- 测试类
/** * @ClassName: Test * @description: guava * @author: zdp * @create: 2022-09-10 17:08 **/ public class Test { public static void main(String[] args) { EventBus eventBus = new EventBus(); Teacher one = new Teacher("Teacher One...."); Teacher two = new Teacher("Teacher Two...."); //注册观察者 eventBus.register(one); eventBus.register(two); Student student = new Student("zdp", new Student.Question("什么是观察者模式? ")); //推送消息到感兴趣的观察者 eventBus.post(student); } }
五、观察者模式的优缺点
- 优点
- 观察者和被观察者是松耦合(抽象耦合)的,符合依赖倒置原则
- 实现了一对多的通讯机制,支持事件注册机制,当被观察者触发事件时,只有感兴趣的观察者可以接收到通知
- 缺点
- 如果观察者数量过多,则事件通知会耗时较长
- 事件通知呈线性关系,如果其中一个观察者处理事件卡壳,会影响后续的观察者接收该事件
- 如果观察者和被观察者之间存在循环依赖,则可能造成两者之间的循环调用,导致系统崩溃
更多推荐
已为社区贡献4条内容
所有评论(0)