读书笔记:headfirst 设计模式 ,大话设计模式,相关技术博客等
观察者模式
概念:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会受到通知并且自动更新
- 观察者模式又称发布/订阅(publish/subscribe)模式
- 观察者模式定义了对象之间的一对多关系
- 主题(可观察者)用一个共同的接口来更新观察者
- 观察者和可观察者之间用松耦合方式结合,双方不知道对方的细节
- 使用观察者模式,可以从被观察者处push推或pull拉数据
观察者模式结构图
代码案例
老师发布作业,老师的每个学生会收到作业通知
// 观察者模式案例测试
public class ObserverTest {
public static void main(String[] args) {
Teacher teacher = new Teacher();
// 订阅
Student s1 = new Student("xiaoMing", teacher);
Student s2 = new Student("xiaoZhang", teacher);
Student s3 = new Student("xiaoLi", teacher);
// 发布通知
teacher.setHomework("数学课后习题");
teacher.setHomework("语文课前预习");
}
}
结果:
老师布置作业:数学课后习题
学生_xiaoMing_收到作业:数学课后习题
学生_xiaoZhang_收到作业:数学课后习题
学生_xiaoLi_收到作业:数学课后习题
老师布置作业:语文课前预习
学生_xiaoMing_收到作业:语文课前预习
学生_xiaoZhang_收到作业:语文课前预习
学生_xiaoLi_收到作业:语文课前预习
// 观察者
public interface Observer {
void update(String info);
}
// 主题(被观察者)
public interface Subject {
// 添加观察者
void addObserver(Observer observer);
// 移除观察者
void removeObserver(Observer observer);
// 通知所有观察者
void notifyObserver();
}
// 观察者 学生实现
public class Student implements Observer {
private String name;
// 保存被观察者subject引用,以后可以方便取消订阅
private Teacher teacher;
public Student(String name,Teacher teacher) {
this.name =name;
this.teacher=teacher;
// 建立学生添加到被观察者中
teacher.addObserver(this);
}
@Override
public void update(String info) {
System.out.println("学生_"+name+"_收到作业:"+info);
}
}
// 被观察者 老师 实现
public class Teacher implements Subject {
// 存放观察者
private List<Observer> observers = new ArrayList<Observer>();
// 记录作业
private String homework;
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
int indexOf = observers.indexOf(observer);
if (indexOf>=0) {
observers.remove(indexOf);
}
}
@Override
public void notifyObserver() {
for (Observer observer : observers) {
observer.update(homework);
}
}
// 布置作业,最后调用notifyObserver(),通知所有被观察者更新
public void setHomework(String homework) {
this.homework = homework;
System.out.println("老师布置作业:"+homework);
notifyObserver();
}
}
java内置的观察者模式
使用java.util包的Observer接口与Observable类
// 观察者
public class Student implements Observer {
private String name;
private Observable observable;
public Student(String name,Observable observable) {
this.name =name;
this.observable=observable;
// 建立学生添加到观察者中
observable.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
Teacher teacher=(Teacher) o;
// 拉取数据
System.out.println("PULL:学生_"+name+"_收到作业:"+teacher.getHomework());
// 接收推送数据
System.out.println("PUSH:学生_"+name+"_收到作业:"+arg.toString());
}
}
// 被观察者
public class Teacher extends Observable {
// 记录作业
private String homework;
// 布置作业,最后调用notifyObserver(),通知所有被观察者更新
public void setHomework(String homework) {
this.homework = homework;
System.out.println("老师布置作业:"+homework);
//PS:重点 需要更改状态 ,好让notify知道当它被调用时应该更新观察者
//这样设计更有弹性,可以选择适当的通知观察者 clearChanged()将状态设置回false,hasChanged()获取当前状态
setChanged();
// 推送数据(作业)
notifyObservers(homework);
}
// 用于观察者拉取数据
public String getHomework() {
return homework;
}
}
// 测试
public class ObserverTest {
public static void main(String[] args) {
Teacher teacher = new Teacher();
Student s1 = new Student("学生A", teacher);
Student s2 = new Student("学生B", teacher);
teacher.setHomework("作业1");
teacher.setHomework("作业2");
}
}
结果:
老师布置作业:作业1
PULL:学生_学生B_收到作业:作业1
PUSH:学生_学生B_收到作业:作业1
PULL:学生_学生A_收到作业:作业1
PUSH:学生_学生A_收到作业:作业1
老师布置作业:作业2
PULL:学生_学生B_收到作业:作业2
PUSH:学生_学生B_收到作业:作业2
PULL:学生_学生A_收到作业:作业2
PUSH:学生_学生A_收到作业:作业2
PS: 注意不可以依赖观察者的特定通知次序,一旦被观察者的实现改变,通知次序可能就会发生改变
内置Observable缺陷
- 可观察者是一个类,而不是接口,限制了复用潜力
- setChanged()方法为protected,无法创建实例组合到自定义对象中,只能继承