场景假设
学校每天都有公告通知,改公告信息来源于学校办公室。每个教室有一块小黑板用来显示公告,目标:小黑板显示来自学校办公室的公告
按照以上的需求,我们可以假象出很多方案
方式一:被通知的对象主动的去获取信息:每个班的班长每间隔5分钟去一趟办公室问老师,今天的公告是啥?
方式二:被通知对象被动的收到消息:老师记下了每个班长的电话号码,发布公告的时候群发短信
等等其他方式
可以看出在这种一对多的关系中,老师主动群发消息的方式是简单快捷的
那么在上述方案中,我们用代码来实现
/**
* * 办公室老师具有发布消息的行为,同时他需要知道他每个班长的号码
*/
public class MessageTeacher {
private String message;
public void genrateMessage(){
this.message = "现在时间:" + System.currentTimeMillis();
publishClassRoomList();
}
//班长花名册
private List<ClassRoom> classRoomList = new ArrayList<>();
//老师需要加下班长的号码
public void addClassRoom(ClassRoom classRoom){
classRoomList.add(classRoom);
}
private void publishClassRoomList(){
for(ClassRoom classroom : classRoomList){
classroom.displayMeaagse(message);
}
}
}
另外定义教室ClassRoom,教室的行为是显示老师发布的消息,由此可定义:
/**
* 教室的父类
*/
public abstract class ClassRoom {
protected String title;
public void displayMeaagse(String msg) {
System.out.println(title + msg);
}
}
public class ClassRoomA extends ClassRoom{
public ClassRoomA(){
title = this.getClass().getSimpleName();
}
}
public class ClassRoomB extends ClassRoom{
public ClassRoomB(){
title = this.getClass().getSimpleName();
}
}
发布:
public static void main(String[] args) {
MessageTeacher messageTeacher = new MessageTeacher();
messageTeacher.addClassRoom(new ClassRoomA());
messageTeacher.addClassRoom(new ClassRoomB());
messageTeacher.genrateMessage();
}
整个发布公告的具体过程:
老师需要记下每个班长的号码--》老师有的新的公告--》群发消息--》每个班级显示公告
在这种一对多的关系中,我们可以用一个固定的模式将上面的逻辑抽象出来。
那么按照如上 一个对象的改变时,通知依赖他的多个对象,用抽象耦合的关系来规范代码,就叫观察者模式
定义
在上述的这种行为中,当一个对象被修改时,则会自动通知依赖它的对象,将这种行为在代码做设计规范,即为观察者模式。
角色
老师的公告------>被观察者
每一个班级班长----->观察者
我们可以抽象定义出这种一对多依赖关系,将被观察者和观察者的依赖关系抽象出来,那么被观察者和观察者一种抽象耦合的关系。
2个接口,2个类
1.抽象主题(Subject)角色(接口):主题角色将所有对观察者对象的引用保存在一个集合中,每个主题可以有任意多个观察者。 抽象主题提供了增加和删除观察者对象的接口。
2.抽象观察者(Observer)角色(接口):为所有的具体观察者定义一个接口,在观察的主题发生改变时更新自己。
3.具体主题(ConcreteSubject)角色(1个):存储相关状态到具体观察者对象,当具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。
4.具体观察者(ConcretedObserver)角色(多个):存储一个具体主题对象,存储相关状态,实现抽象观察者角色所要求的更新接口,以使得其自身状态和主题的状态保持一致。
实现
观察者模式实现过程
观察者模式的 UML 图图片来源: https://www.runoob.com/design-pattern/observer-pattern.html
那么我们按照这种抽象耦合的模式来实现一开始老师发布消息的场景。
一、定义被观察者抽象接口
/**
* 主题
*/
public interface Subject {
// 添加观察者
void registerObserver(Observer o);
// 删除观察者
void removeObserver(Observer o);
// 通知
void notifyObservers();
}
二、定义观察者接口
/**
* 观察者超类
*/
public interface Observer {
public void update(float temp, float humidity, float pressure);
}
三、定义具体的被观察者
public class MessageTeacherSubject implements Subject {
private String message;
public void genrateMessage(){
this.message = "现在时间:" + System.currentTimeMillis();
}
private List<Observer> observers = new ArrayList<>();
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
int i = observers.indexOf(0);
if (i >= 0) {
observers.remove(0);
}
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(message);
}
}
}
四、定义具体的观察者
public class ClassRoomObserverB extends ClassRoom implements Observer {
public ClassRoomObserverB(){
title = "Observer:" + this.getClass().getSimpleName();
}
@Override
public void update(String action) {
displayMeaagse(action);
}
}
public class ClassRoomObserverA extends ClassRoom implements Observer {
public ClassRoomObserverA(){
title = "Observer:" + this.getClass().getSimpleName();
}
@Override
public void update(String action) {
displayMeaagse(action);
}
}
五、注册依赖
MessageTeacherSubject messageTeacherSubject = new MessageTeacherSubject();
messageTeacherSubject.genrateMessage();
messageTeacherSubject.registerObserver(new ClassRoomObserverA());
messageTeacherSubject.registerObserver(new ClassRoomObserverB());
messageTeacherSubject.notifyObservers();
执行结果:
Observer:ClassRoomObserverA现在时间:1594870164213
Observer:ClassRoomObserverB现在时间:1594870164213
使用场景
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
缺点
1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
参考: https://www.runoob.com/design-pattern/observer-pattern.html
网友评论