java-观察者模式

作者 uunnfly 日期 2019-04-03
java-观察者模式

观察者模式是指一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。比如说软件开发时的热更新功能,修改了代码之后能够自动重新部署。

原理


(idea自动生成的类图)

Subject:被观察对象的抽象类,持有一个List保存观察者对象的引用,可以添加、删除观察者对象,并且在发生变化时通知所有的观察者。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public abstract class Subject {
private List<Observer> observerList = new ArrayList<Observer>();

public void addObserver(Observer o){
observerList.add(o);
}

public void deleteObserver(Observer o){
observerList.remove(o);
}


public void notifyObservers(){
for(Observer o : observerList){
//把自身的引用传给观察者
o.update(this);
}
}
}

Observer: 观察者的接口,有一个update()方法来给被观察者调用,接受Subject的更新通知

1
2
3
public interface Observer {
void update(Subject subject);
}

ConcretSubject: Subject的具体类,代表具体的被观察对象的类,状态发生改变时需要调用notifyObservers()方法通知观察者

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ConcreteSubject extends Subject{
private String state;

public String getState() {
return state;
}

public void change(String newState){
state = newState;
System.out.println("被观察对象的状态为:" + state);
this.notifyObservers();
}
}

ConcretObserver: Observer的具体类,接受目标对象(被观察者)的更新通知,并作出反应。

1
2
3
4
5
6
7
8
9
public class ConcreteObserver implements Observer {
private Subject subject;

//接收目标对象的引用
public void update(Subject subject) {
this.subject = subject;
System.out.println("观察者观察到状态改变,状态为: " + ((ConcreteSubject)subject).getState());
}
}

我们测试一下:

1
2
3
4
5
6
7
8
9
10
public class Test {

public static void main(String[] args){
ConcreteSubject subject = new ConcreteSubject();
Observer observer = new ConcreteObserver();
subject.addObserver(observer);
subject.change("hello,this is my new state");
}

}

结果:

1
2
被观察对象的状态为:hello,this is my new state
观察者观察到状态改变,状态为: hello,this is my new state

Jdk中的工具

jdk已经为我们提供了目标对象的抽象类和观察者的接口,分别为java.util.Observable,java.util.Observer。我们只要继承/实现抽象类与接口就行了。下面看一个使用的例子:

假设一个场景:花匠养花。当花干枯的时候花匠需要浇水,当花盛开的时候花匠觉得开心。

花是被观察的,也就是目标对象,继承Observable

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Bloom extends Observable {
public void dry(){
System.out.println("the bloom is dry.");
setChanged();
notifyObservers("dry");
}

public void flower(){
System.out.println("the bloom is flowering.");
setChanged();
notifyObservers("flower");
}
}

这里与上面代码不同的是:必须调用serChanged()方法才能改变状态,notifyObservers方法通知观察者时可以加一个参数。

花匠是观察者,实现Observer接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Gardener implements Observer {
public void update(Observable o, Object arg) {
if(arg.equals("dry")) water();
if(arg.equals("flower")) happy();
}

private void water(){
System.out.println("The gardener starts to water.");
}

private void happy(){
System.out.println("The gardener becomes happy.");
}
}

这里的update方法会接受两个参数,一个是发生改变的目标对象的引用,另一个是notifyObservers方法传入的参数。
我们测试一下:

1
2
3
4
5
6
7
8
9
10
public class TestUseJdk {
public static void main(String[] args){
Bloom bloom = new Bloom();
Gardener gardener = new Gardener();

bloom.addObserver(gardener);
bloom.dry();
bloom.flower();
}
}

结果

1
2
3
4
the bloom is dry.
The gardener starts to water.
the bloom is flowering.
The gardener becomes happy.

如果不进行setChanged()会怎么样?

1
2
3
4
5
 public void flower(){
System.out.println("the bloom is flowering.");
// setChanged();
notifyObservers("flower");
}

结果:

1
2
3
the bloom is dry.
The gardener starts to water.
the bloom is flowering.

花匠(观察者)不会收到通知

jdk还提供clearChanged()方法,看看有什么用

1
2
3
4
5
6
public void flower(){
System.out.println("the bloom is flowering.");
setChanged();
clearChanged();
notifyObservers("flower");
}

结果:

1
2
3
the bloom is dry.
The gardener starts to water.
the bloom is flowering.

clearChanged()会视为目标对象未发生改变,从而不会给观察者发送通知

事实上,当通知完观察者后会标记自身为未发生改变

1
2
3
4
5
6
7
8
public void flower(){
System.out.println("the bloom is flowering.");
setChanged();
//hasChanged()方法返回自上次通知完观察者之后是否状态有过改变
System.out.println("before notify:" + hasChanged());
notifyObservers("flower");
System.out.println("after notify:" + hasChanged());
}

结果

1
2
3
4
the bloom is flowering.
before notify:true
The gardener becomes happy.
after notify:false

本文例子源码 https://github.com/UUNNFLY/design_pattern

参考链接

博客园—《JAVA与模式》之观察者模式
GeeksforGeeks- Java.util.Observable class in Java
Observable (Java Platform SE 7 ) - Oracle Docs