您的位置:首页 > 其它

设计模式之观察者模式

2018-01-13 17:20 162 查看
在学习java AWT或者Swing的时候,我们知道,如果为按钮添加了监听器,那么在我们点击按钮的时候,就可以触发一定的事件。大致过程如下:当点击一个按钮的时候就会产生一个事件,然后检查是否有与该按钮关联的事件处理器(实际上就是一个方法),如果没有,那么什么都不执行;如果有,就会将该事件传递给与该按钮相关联的处理器方法,作为该方法的参数,之后该事件处理器方法就会“自动”得到调用,并且该方法可以使用传递过来的ActionEvent对象进而获得事件发生时与该事件及事件源相关联的那些信息。

其中,“自动调用”是不准确的,确切地说来还是按钮去调用了与之关联的处理器方法。

这其中,也涉及到了一种设计模式————观察者模式

观察者模式:一对多的依赖关系,多个观察者对象同时监听某一主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,让他们能够自动更新自己。

观察者模式的组成:

1.抽象主题角色(也就是被观察者):把所有观察者对象的引用保存在一个集合中,每个抽象主题角色都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类或接口来实现。

2.具体主题角色:在具体主题内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个子类实现。

3.抽象观察者角色:为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。

4.具体观察者角色:该角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体观察角色可以保存一个指向具体主题角色的引用。通常用一个子类实现。

实现自己的观察者模式:

/**
* 主题抽象角色:也称为被观察者
* addWatcher添加观察者
* removeWatcher删除观察者
* notifyWatcher通知观察者状态更新
*/
public interface Watched {
public void addWatcher(Watcher watcher);
public void removeWatcher(Watcher watcher);
public void notifyWatcher(String str);
}


/**
* 观察者抽象角色
* update方法用于更新状态,使得观察者状态与主题状态相协调
*/
public interface Watcher {
public void update(String str);
}


/**
* 具体主题角色:内部维护了对观察者角色的引用
* notifyWatcher实质上是通过调用所有观察者的update方法实现了观察者状态的更新
* 但对于用户来说,好像是主题发出状态改变,观察者“自动”更新状态
*/
public class ConcreateWatched implements Watched {
private List<Watcher> list = new ArrayList<Watcher>();
@Override
public void addWatcher(Watcher watcher) {
list.add(watcher);
}

@Override
public void removeWatcher(Watcher watcher) {
list.remove(watcher);
}

@Override
public void notifyWatcher(String str) {
//遍历所有的观察者引用,调用观察者中的方法
for(Watcher w : list){
w.update(str);
}
}
}


/**
*具体观察者角色
*/
public class ConcreateWatcher implements Watcher {
@Override
public void update(String str) {
System.out.println(str);
}
}


使用示例:

public class Client {
public static void main(String[] args){
Watched girl = new ConcreateWatched();

Watcher boy1 = new ConcreateWatcher();
Watcher boy2 = new ConcreateWatcher();
Watcher boy3 = new ConcreateWatcher();

girl.addWatcher(boy1);
girl.addWatcher(boy2);
girl.addWatcher(boy3);

girl.notifyWatcher("I am so happy.");

girl.removeWatcher(boy1);
girl.removeWatcher(boy2);
girl.notifyWatcher("No happy");
}
}


观察者模式在Java语言中的地位极其重要,JDK也提供了对观察者模式的内置支持

Observable类:该类用于创建可以观测到你的程序中其他部分的子类。也就是上述所说的主题角色,也是被观察者。当这种子类的对象发生变化时,观测类被通知。观测类必须实现定义了update()方法的Observer接口,也就是观察者角色。当一个观测程序被通知到另一个被观测对象的改变时,update()方法被调用。

一个被观测的对象(主题角色)必须服从下面两个简单的规则:

1.如果它被改变了,必须调用setChanged()

2.当它准备通知观察者它的改变时,必须调用notifyObservers()方法。这会使得在观察者对象中update()方法的调用。

当对象在调用notifyObservers()方法之前,没有调用setChanged()方法,就不会有什么动作发生(查看源码可知原因)。在update()被调用之前,被观测对象必须调用setChanged()和notifyObservers()两种方法

public void notifyObservers(Object arg) {
/*
* a temporary array buffer, used as a snapshot of the state of
* current Observers.
*/
Object[] arrLocal;

synchronized (this) {
/* We don't want the Observer doing callbacks into
* arbitrary code while holding its own Monitor.
* The code where we extract each Observable from
* the Vector and store the state of the Observer
* needs synchronization, but notifying observers
* does not (should not).  The worst result of any
* potential race-condition here is that:
* 1) a newly-added Observer will miss a
*   notification in progress
* 2) a recently unregistered Observer will be
*   wrongly notified when it doesn't care
*/
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}

for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}


如果changed(初始值为false)为false,那么直接返回,不会执行通知操作。所以需要改变changed的状态,则需要setChange()方法,将changed改为true。

应用:编写一个程序,声明一个类,该类继承自Observable(因此该类是个主题角色),有一个int类型的变量,初始值为10,编写一个for循环,将该数字每次递减1,一直到0为止,每次变化时,都将该数字传递给它的观察者,观测者会打印出该数字;第二个观察者在该数字变为I5之后开始打印该数字。

/**
* 被观测类(主题角色),需继承Observable
*/
public class MyTheme extends Observable {
public void count(int num){
for (;num>=0;num--){
//使得changed属性为true,从而可以使得主题可以向观察者发送消息
this.setChanged();
//每一次技术都调用notifyObservers()
this.notifyObservers(num);
}
}
}


/**
* 观察者1:打印主题角色的所有计数
*/
public class Watcher1 implements Observer {
@Override
public void update(Observable o, Object arg) {
System.out.println("Watcher1 "+arg);
}
}


/**
* 观察者2:打印主题角色的计数,当且仅当技术不大于5
*/
public class Watcher2 implements Observer {
@Override
public void update(Observable o, Object arg) {
if(((Integer)arg).intValue() <= 5){
System.out.println("Watcher2:"+arg);
}
}
}


写个main方法用一下:

public class Client {
public static void main(String[] args){
MyTheme theme = new MyTheme();
Watcher1 obs1 = new Watcher1();
Watcher2 obs2 = new Watcher2();
theme.addObserver(obs1);
theme.addObserver(obs2);
theme.count(10);
}
}


输出结果:

Watcher 1:10

Watcher 1:9

Watcher 1:8

Watcher 1:7

Watcher 1:6

Watcher 2:5

Watcher 1:5

Watcher 2:4

Watcher 1:4

Watcher 2:3

Watcher 1:3

Watcher 2:2

Watcher 1:2

Watcher 2:1

Watcher 1:1

Watcher 2:0

Watcher 1:0

从结果可以看出,观察者2只有在计数不大于5时才输出
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: