设计模式之观察者模式
本篇是设计模式系列博客的第四篇,本篇主要学习设计模式中的第二个行为型模式—观察者模式。
- 什么是观察者模式?
- 模式的结构?
- 模式自定义实现和JDK自带实现?
- 推模型和拉模型?
- JDK源码分析?
1 什么是观察者模式?
引用百度百科中的解释:
观察者模式(有时又被称为模型-视图(View)模式、源-收听者(Listener)模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。
简单点概括成通俗的话来说,就是一个类管理着所有依赖于它的观察者类,并且在它状态变化时会主动给这些依赖它的类发出通知。当然后面我们也可以看到除了主动推消息外也可以拉消息。
2 模式的结构
把它用图表示如下:
可以看到,我们的被观察者类Observable只关联了一个Observer的列表,然后在自己状态变化时,使用notifyObservers方法通知这些Observer,具体这些Observer都是什么,被观察者是不关心也不需要知道的。
上面就将观察者和被观察者二者的耦合度降到很低了,而我们具体的观察者是必须要知道自己观察的是谁,所以它依赖于被观察者。
3 模式实现
3.1 自定义观察者模式
下面LZ给写出一个很简单的观察者模式-拉模型,来使用JAVA代码简单诠释一下上面的类图。
1 | //这个接口是为了提供一个统一的观察者做出相应行为的方法 |
具体的观察者1:
1 | public class ConcreteObserver1 implements Observer{ |
具体的观察者2:
1 | public class ConcreteObserver2 implements Observer{ |
下面是被观察者,它有一个观察者的列表,并且有一个通知所有观察者的方法,通知的方式就是调用观察者通用的接口行为update方法。下面我们看它的代码。
public class Observable {
List<Observer> observers = new ArrayList<Observer>();
public void addObserver(Observer o){
observers.add(o);
}
public void changed(){
System.out.println("我是被观察者,我已经发生变化了");
notifyObservers();//通知观察自己的所有观察者
}
public void notifyObservers(){
for (Observer observer : observers) {
observer.update(this);
}
}
}
这里面很简单,新增两个方法,一个是为了改变自己的同时通知观察者们,一个是为了给客户端一个添加观察者的公共接口。
下面我们使用客户端调用一下,看一下客户端如何操作。
1 | public class Client { |
输出结果:1
2
3
4
5我是观察者,我已经发生了变化
观察者1观察到Obserable发生了变化
观察者1执行动作
观察者2观察到Obserable发生了变化
观察者2执行动作
3.2 JDK自带的观察者模式
JDK提供了一个Observable(被观察者)抽象类,和一个Observer(观察者)接口。继承前者就可以被当做一个Subject主题,实现后面接口就可以成为Subject的一个观察者。主题需要显示调用setChanged来告诉Observable,我们状态改变需要通知给观察者,也提供了update的重载方法,可以实现推拉两种模式。观察者只需要实现update方法就可以获取被观察者的状态。
4 推模型和拉模型?
4.1 推模型
主题对象向观察者推送主题的详细信息,不管观察者是否需要,推送的信息通常是主题对象的全部或部分数据。
1 | //这个接口是为了提供一个统一的观察者做出相应行为的方法 |
具体的观察者:
1 | public class ConcreteObserver2 implements Observer{ |
下面是被观察者,它有一个观察者的列表,并且有一个通知所有观察者的方法,通知的方式就是调用观察者通用的接口行为update方法。下面我们看它的代码。
public class Observable {
private String state;
public void setState(String state){
system.out.pritln("被观察者改变了状态"+state);
this.state =state;
}
public String getState(){
return state;
}
List<Observer> observers = new ArrayList<Observer>();
public void addObserver(Observer o){
observers.add(o);
}
public void notifyObservers(){
for (Observer observer : observers) {
observer.update();
}
}
}
这里面很简单,新增两个方法,一个是为了改变自己的同时通知观察者们,一个是为了给客户端一个添加观察者的公共接口。
下面我们使用客户端调用一下,看一下客户端如何操作。
1 | public class Client { |
输出结果:1
2
3被观察者改变了状态change state
观察者观察到change state发生了变化
4.2 拉模型
主题对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到主题对象中获取,相当于是观察者从主题对象中拉数据。一般这种模型的实现中,会把主题对象自身通过update()方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。
这种模式在文章的前面已经给出栗子了。
5. JDK源码分析
其实源码思想跟我们前面自己实现的差别不大只是做了一个封装。先看主题抽象类。代码如下(代码不多我全部贴上,写上必要的注释):
1 | public class Observable { |
现在是观察者接口:
1 | public interface Observer { |
6 总结
观察者模式所欠缺的是设计上的问题,即观察者和被观察者是多对一的关系,那么反过来的话,就无法支持了。
观察者模式还有一个缺点就是,每一个观察者都要实现观察者接口,才能添加到被观察者的列表当中,假设一个观察者已经存在,而且我们无法改变其代码,那么就无法让它成为一个观察者了,不过这个我们依然可以使用适配器模式解决。但是还有一个问题就不好解决了,就是假如我们很多类都是现成的,当被观察者发生变化时,每一个观察者都需要调用不同的方法,那么观察者模式就有点捉襟见肘的感觉了,我们必须适配每一个类去统一他们变化的方法名称为update,这是一个很可怕的事情。