介绍
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在事件源里有一个 ArrayList 存放观察者们。
应用实例: 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景:
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
注意事项: 1、JAVA 中已经有了对观察者模式的支持类。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
在观察者模式中最关键的有三个部分:事件源(Source)、事件(Event)以及观察者(Observer),Observer监听Event,Event作用于Source,而在Source中聚合了各种Observer形成一个链条(类似于责任链),以便在做出相应的Event时,通过观察者链条来对Event做出各种响应。
实例演示
我们在网页上点击各种的按钮都会进行响应,我们来模拟一下点击按钮的响应事件,首先俩看类图:
一、首先来定义事件(Event)——ActionEvent类:
class ActionEvent {
Object source;
public ActionEvent(long when, Object source) {
super();
this.source = source;
}
// 获取事件源
public Object getSource() {
return source;
}
}
在Event类中定义了getSource()方法,是为了方便在观察者在响应事件的过程中需要调用事件源。
二、接下来定义观察者的接口以及相应的实现类(观察者的具体实现):
interface ActionListener{
void actionPerformed(ActionEvent actionEvent);
}
class MyActionListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent actionEvent) {
System.out.println("button pressed!");
}
}
class MyActionListener2 implements ActionListener{
@Override
public void actionPerformed(ActionEvent actionEvent) {
System.out.println("button pressed2!");
}
}
三、接下来定义事件源(Source)——Button类:
class Button{
private List<ActionListener> actionListeners = new ArrayList<>();
public void addActionListener(ActionListener l){
actionListeners.add(l);
}
public void buttonPressed(){
//当发生点击按钮的事件时,获取事件,并传入观察者
ActionEvent actionEvent = new ActionEvent(System.currentTimeMillis(), this);
for(ActionListener actionListener:actionListeners){
actionListener.actionPerformed(actionEvent);
}
}
}
五、最后在Main方法中我们新建一个按钮Button,并对Button添加我们定义好的两个观察者,并点击按钮:
public class Test {
public static void main(String[] args) {
Button b = new Button();
b.addActionListener(new MyActionListener());
b.addActionListener(new MyActionListener2());
//按下按钮
b.buttonPressed();
}
}
button pressed!
button pressed2!
可以看到当按钮的点击事件发生时,观察者成功进行了相应。
总结
我们发现观察者和责任链模式有点相似,但是责任链不是对Event进行响应的,而是对一个具体的事件源进行一连串的处理,而观察者是通过处理发生在事件源上面的Event进行处理,在有些时候这两个模式会混合使用,比如说Source产生Event时会将Event传给一个责任链条,进行处理。在责任链模式中链子是可以断掉的。
在平常我们用到的各种监听以及钩子函数(hook)都运用了观察者模式。