观察者模式是日常编程中常见的设计模式,主要用于在一对多的关系中,一个类状态或者数据的变化,多个类需要跟着改变的场景,这种场景中变化是一种单向的依赖关系。在实现思路上,就是让被观察的类持有一个需要通知的类的列表,当被观察者变化的时候,就可以挨个去通知观察者对象,让其根据自身的逻辑去变化。观察者模式本质上是一种事件中断机制,平时观察者不会时时刻刻去关注被观察对象,当被观察对象有变化的时候,就会自动通知过来,然后观察者再去处理对应的逻辑。
观察者模式结构图
- Subject:目标对象,通常会持有一个观察者的列表,并提供添加和删除观察者的功能
- ConcreteObject:观察者的具体对象,在这里涉及到需要通知观察者的地方,会调用观察者的通知接口来通知观察者
- Observer:观察者,这里会定义一个统一通知的方法,方便被观察对象发生变化的时候来调用
- ConcreteObserver:观察者的具体实现
报纸订阅示例
我们来模拟一个报纸订阅的场景,当报纸的内容更新的时候,就需要把新的报纸发给订阅的读者。
1. 观察者接口
/**
* 观察者接口
*/
public interface Observer {
/**
* 数据更新后通知结构
*
* @param content :
*/
void notify(String content);
}
2. 读者类
/**
* 观察者的具体实现类
*/
public class Reader implements Observer {
private String name;
public Reader(String name) {
this.name = name;
}
@Override
public void notify(String content) {
System.out.println("读者【 " + this.name + " 】接收到新的内容数据: " + content);
}
}
3. 目标对象
/**
* 观察的目标对象
*/
public abstract class Subject {
protected List<Observer> observerList;
public Subject(){
this.observerList = new LinkedList<>();
}
/**
* 追加观察者
* @param observer :
*/
public void attach(Observer observer){
this.observerList.add(observer);
}
/**
* 通知所有的观察者
*/
public abstract void notifyObservers();
}
4. 报纸对象
/**
* 被观察对象的具体实现类
*/
public class Newspaper extends Subject {
private String content;
public void updateContent(String content) {
this.content = content;
// 更新数据后通知所有的观察者
notifyObservers();
}
@Override
public void notifyObservers() {
for (Observer observer : super.observerList) {
observer.notify(this.content);
}
}
}
5. 测试方法和测试结果
public class Client {
@Test
public void testObserver(){
Newspaper newspaper = new Newspaper();
//读者订阅报纸
newspaper.attach(new Reader("张三"));
newspaper.attach(new Reader("李四"));
newspaper.attach(new Reader("王五"));
newspaper.attach(new Reader("Tom"));
System.out.println("-----------------报纸出版----------------");
newspaper.updateContent("第一版报纸数据");
System.out.println("-----------------报纸出版----------------");
newspaper.updateContent("第二版报纸");
}
}
测试结果:
-----------------报纸出版----------------
读者【 张三 】接收到新的内容数据: 第一版报纸数据
读者【 李四 】接收到新的内容数据: 第一版报纸数据
读者【 王五 】接收到新的内容数据: 第一版报纸数据
读者【 Tom 】接收到新的内容数据: 第一版报纸数据
-----------------报纸出版----------------
读者【 张三 】接收到新的内容数据: 第二版报纸
读者【 李四 】接收到新的内容数据: 第二版报纸
读者【 王五 】接收到新的内容数据: 第二版报纸
读者【 Tom 】接收到新的内容数据: 第二版报纸
5.示例结构图
6.示例总结
对比观察者模式的原始结构图和上面的示例结构图,我们可以发现原始结构图中给观察者传递的是被观察者对象,而我们示例中却是一个String。这就体现了观察者模式中的推模型和拉模型的区别,所谓的推模型,就像我们示例中一样,把具体的数据发给你,这种场景适用于观察者需要的数据比较少的情况,可以明确定义出具体的数据。所谓的拉模型就是被观察者把自己的引用传给你,想要啥,你自己去取,这种方式适用于观察者取的数据比较多,而且观察者可信的情况,因为传递过来的是引用,理论上是有被修改的风险。在具体的实践中需要根据需要选择要用拉模型还是推模型。
后记
个人总结,欢迎转载、评论、批评指正