观察者模式定义如下:定义对象的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖它的对象都得到通知并被自动更新。
适合观察者模式的情景如下:
- 当某对象数据更新时需要通知其他对象,但该对象又不希望和被通知的对象形成紧耦合
- 当某对象数据更新时,需要让其他对象也各自更新自己的数据,但该对象不知道具体有多少对象需要更新数据
一、问题的提出
在实际生活中,经常会遇到多种对象关注一个对象数据变化的情况。例如:生活中有温度记录仪,当温度发生变化时,需要完成如下功能:记录温度日志,显示温度变化曲线,当温度越界时扬声器发出声音。可能会写出以下程序片段:
while(温度变化){
记录温度日志
显示温度变化曲线
当问温度越界时扬声器发出声音
}
这种方法把所有功能都集成在一起,以致当需求分析发生变化,如:若增加新的我呢度监测功能,或舍去某一监测功能时,程序都得修改,这是我们所不希望看到的效果。观察者模式是解决这类问题的有效方法。
二、观察者模式
观察者模式适合解决多种对象跟踪一个对象数据变化的程序结构问题,有一个称作“主题”的对象和若干个称作“观察者”的对象。
对于上面的例子,主题就是温度,三个观察者分别是:温度日志,温度曲线,温度报警。
一些重要结论:
- 主题要知道有哪些观测者对其进行监测,因此主题类中一定有一个集合类成员变量,包含了观测者的对象集合。
- 既然包含了观测者的对象集合,那么,观测者就一定是多态的,有共同的父类接口
- 主题完成的主要功能是:可以添加观测者,可以撤销观测者,可以向观测者发消息,引起观测者响应。这三个功能是固定的,因此主题类可以从固定的接口派生。
编制观察者设计模式,要完成以下功能类的编制:
- 主题ISubject接口定义
- 主体类编制
- 观察者接口IObserver定义
- 观察者类实现
(1)观察者接口
public interface IObserver {
public void refresh(String data);
}
(2)主题接口
public interface ISubject {
//注册观察者
public void register(IObserver observer);
//撤销观察者
public void unregister(IObserver observer);
//通知所有观察者
public void notifyObservers();
}
(3)主题实现类
public class Subject implements ISubject{
//观察者维护向量
private Vector<IObserver> vector = new Vector<>();
//主题中心数据
private String data;
@Override
//添加观察者
public void register(IObserver observer) {
vector.add(observer);
}
@Override
//删除观察者
public void unregister(IObserver observer) {
if (vector.contains(observer)){
vector.remove(observer);
}
}
@Override
public void notifyObservers() {
//取到所有的观察者
for (int i = 0; i < vector.size(); i++) {
IObserver observer = vector.get(i);
observer.refresh(data);
}
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}
(4)具体的观察者
public class Observer implements IObserver{
@Override
public void refresh(String data) {
System.out.println("我收到了主题发来的数据:"+ data);
}
}
public class Observer2 implements IObserver{
@Override
public void refresh(String data) {
System.out.println(this+"我收到了主题发来的数据:"+ data);
}
}
(5)测试类
当主题中心数据变化(通过setData方法)后,主题类Subject要调用 notifyObservers() ,通知所有观察者对象接收数据并进行响应。
public class Test {
public static void main(String[] args) {
//生成一个观察者对象
IObserver observer = new Observer();
IObserver observer2 = new Observer2();
//生成主题对象
Subject subject = new Subject();
subject.register(observer);
subject.register(observer2);
subject.setData("hello 观察者们!");
subject.notifyObservers();
}
}
结果:
shejimoshi.guanchazhe.Observer@74a14482我收到了主题发来的数据:hello 观察者们!
shejimoshi.guanchazhe.Observer2@1540e19d我收到了主题发来的数据:hello 观察者们!
一些改进:
上面的例子Subject类中的中心数据data是String类型的,因此IObserver接口中定义的refresh() 方法参数类型也是String类型的。 若data为其他类型,只要把ISubject、IObserver接口改为泛型接口就可以了。
在接口中加入泛型参数T,可以使接口变得更加通用和灵活。泛型参数T可以在接口中用作方法参数类型、返回值类型或接口的属性类型,因此可以在使用该接口的类中指定具体的类型,使得该接口具有更加通用和灵活的特性。
使用泛型参数T可以让接口在不同的场合中使用,而不必为每个场景单独定义一个新的接口。例如,在观察者模式中,被观察的对象可以将任何类型的数据通知给观察者,通过将IObserver接口声明为具有泛型参数T,可以让观察者接口在接收通知时能够处理不同类型的数据。
public interface IObserver<T> {
public void refresh(T data);
}
public interface ISubject<T> {
//注册观察者
public void register(IObserver<T> observer);
//撤销观察者
public void unregister(IObserver<T> observer);
//通知所有观察者
public void notifyObservers();
}
实现类修改示例:
public class Observer<T> implements IObserver<T>{
@Override
public void refresh(T data) {
System.out.println(this+"我收到了主题发来的数据:"+ data);
}
}