情景:
气象站会实时发布气象数据,要求创建布告板,并把气象站发布的数据显示出来。
布告板会有很多,随时回添加一个或删除一个,而每个布告板显示的格式也不尽相同。
观察者模式:定义了对象之间的一对多的依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
被观察对象称作主题(Subject),依赖主题的对象称作观察者(Observer)。
当两个对象之间松耦合,它们依然可以交互,但是不太清除彼此之间的细节。
观察者模式提供了一种对象设计,让主题和观察者之间松耦合。
类图:
设计原则:为了交互对象之间的松耦合设计而努力。
气象站的实现:
主题接口:
观察者接口:
update( temp, humidity,
主题(气象站)实现:
<span style="color: #0000ff;">public <span style="color: #0000ff;">class WeatherData <span style="color: #0000ff;">implements<span style="color: #000000;"> Subject {
</span><span style="color: #0000ff;">private</span> ArrayList<Observer><span style="color: #000000;"> observers;
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">float</span><span style="color: #000000;"> temperature;
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">float</span><span style="color: #000000;"> humidity;
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">float</span><span style="color: #000000;"> pressure;
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> WeatherData() {
observers </span>= <span style="color: #0000ff;">new</span> ArrayList<><span style="color: #000000;">();
}
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> registerObserver(Observer o) {
observers.add(o);
}
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> removeObserver(Observer o) {
</span><span style="color: #0000ff;">int</span> i =<span style="color: #000000;"> observers.indexOf(o);
</span><span style="color: #0000ff;">if</span> (i >= 0<span style="color: #000000;">) {
observers.remove(i);
}
}
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> notifyObservers() {
</span><span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = 0; i < observers.size(); i++<span style="color: #000000;">) {
Observer observer </span>=<span style="color: #000000;"> observers.get(i);
observer.update(temperature,humidity,pressure);
}
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> measurementsChanged() {
notifyObservers();
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> setMeasurements(<span style="color: #0000ff;">float</span> temperature,<span style="color: #0000ff;">float</span><span style="color: #000000;"> pressure) {
</span><span style="color: #0000ff;">this</span>.temperature =<span style="color: #000000;"> temperature;
</span><span style="color: #0000ff;">this</span>.humidity =<span style="color: #000000;"> humidity;
</span><span style="color: #0000ff;">this</span>.pressure =<span style="color: #000000;"> pressure;
measurementsChanged();
}
}
观察者(布告板)实现:
<span style="color: #0000ff;">public <span style="color: #0000ff;">class CurrentConditionsDisplay <span style="color: #0000ff;">implements<span style="color: #000000;"> Observer,DisplayElement {
<span style="color: #0000ff;">private <span style="color: #0000ff;">float<span style="color: #000000;"> temperature;
<span style="color: #0000ff;">private <span style="color: #0000ff;">float<span style="color: #000000;"> humidity;
<span style="color: #0000ff;">private <span style="color: #0000ff;">float<span style="color: #000000;"> pressure;
<span style="color: #0000ff;">private<span style="color: #000000;"> Subject weatherData;
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">float</span><span style="color: #000000;"> temperature;
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">float</span><span style="color: #000000;"> humidity;
</span><span style="color: #0000ff;">private</span><span style="color: #000000;"> Subject weatherData;
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> CurrentConditionsDisplay(Subject weatherData) {
</span><span style="color: #0000ff;">this</span>.weatherData =<span style="color: #000000;"> weatherData;
weatherData.registerObserver(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">);
}
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> display() {
System.out.println(</span>"Current conditions: " + temperature +
"F degrees and " + humidity + "% humdity."<span style="color: #000000;">);
}
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> update(<span style="color: #0000ff;">float</span> temperature,<span style="color: #0000ff;">float</span><span style="color: #000000;"> pressure) {
</span><span style="color: #0000ff;">this</span>.temperature =<span style="color: #000000;"> temperature;
</span><span style="color: #0000ff;">this</span>.humidity =<span style="color: #000000;"> humidity;
display();
}
}
……………………
<span style="color: #0000ff;">private <span style="color: #0000ff;">float<span style="color: #000000;"> temperature;
<span style="color: #0000ff;">private <span style="color: #0000ff;">float<span style="color: #000000;"> humidity;
<span style="color: #0000ff;">private <span style="color: #0000ff;">float<span style="color: #000000;"> pressure;
<span style="color: #0000ff;">private<span style="color: #000000;"> Subject weatherData;
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> ChineseDisplay(Subject data) {
</span><span style="color: #0000ff;">this</span>.weatherData =<span style="color: #000000;"> data;
weatherData.registerObserver(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">);
}
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> display() {
System.out.println(</span>"天气状况: 气温:" + temperature +
"F, 湿度:" + humidity + "%, 气压:" + pressure + " Pa。"<span style="color: #000000;">);
}
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> update(<span style="color: #0000ff;">float</span> temperature,<span style="color: #0000ff;">float</span><span style="color: #000000;"> pressure) {
</span><span style="color: #0000ff;">this</span>.temperature =<span style="color: #000000;"> temperature;
</span><span style="color: #0000ff;">this</span>.humidity =<span style="color: #000000;"> humidity;
</span><span style="color: #0000ff;">this</span>.pressure =<span style="color: #000000;"> pressure;
display();
}
}
测试类:
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {
WeatherData weatherData </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> WeatherData();
CurrentConditionsDisplay conditionsDisplay </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> CurrentConditionsDisplay(weatherData);
StatisticsDisplay statisticsDisplay </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> StatisticsDisplay(weatherData);
ChineseDisplay chineseDisplay </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> ChineseDisplay(weatherData);
weatherData.setMeasurements(</span>80,65,30.4f<span style="color: #000000;">);
System.out.println();
weatherData.setMeasurements(</span>82,70,29.2f<span style="color: #000000;">);
System.out.println();
weatherData.setMeasurements(</span>78,90,29.2f<span style="color: #000000;">);
}
}
输出:
Current conditions: 80.0F degrees and 65.0%80.0F, 湿度:65.0%, 气压:30.4Current conditions: 82.0F degrees and 70.0%<span style="color: #000000;"> humdity.
天气状况: 气温:82.0F, 湿度:70.0%, 气压:29.2 Pa。
天气状况: 气温:82.0F, 湿度:70.0%, 气压:29.2 Pa。
Java中自带Observable类和Observer类,可以通过继承这两个类来实现观察者模式
主题:
<span style="color: #0000ff;">public <span style="color: #0000ff;">class WeatherData <span style="color: #0000ff;">extends<span style="color: #000000;"> Observable {
<span style="color: #0000ff;">private <span style="color: #0000ff;">float<span style="color: #000000;"> temperature;
<span style="color: #0000ff;">private <span style="color: #0000ff;">float<span style="color: #000000;"> humidity;
<span style="color: #0000ff;">private <span style="color: #0000ff;">float<span style="color: #000000;"> pressure;
<span style="color: #0000ff;">private <span style="color: #0000ff;">float<span style="color: #000000;"> temperature;
<span style="color: #0000ff;">private <span style="color: #0000ff;">float<span style="color: #000000;"> humidity;
<span style="color: #0000ff;">private <span style="color: #0000ff;">float<span style="color: #000000;"> pressure;
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> WeatherData() {}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> measurementsChanged() {
setChanged();
notifyObservers();
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> setMeasurements(<span style="color: #0000ff;">float</span> temperature,<span style="color: #0000ff;">float</span><span style="color: #000000;"> pressure) {
</span><span style="color: #0000ff;">this</span>.temperature =<span style="color: #000000;"> temperature;
</span><span style="color: #0000ff;">this</span>.humidity =<span style="color: #000000;"> humidity;
</span><span style="color: #0000ff;">this</span>.pressure =<span style="color: #000000;"> pressure;
measurementsChanged();
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">float</span><span style="color: #000000;"> getTemperature() {
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> temperature;
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">float</span><span style="color: #000000;"> getHumidity() {
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> humidity;
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">float</span><span style="color: #000000;"> getPressure() {
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> pressure;
}
}
观察Observable源码setChanged方法
=
有时候改变需要自己定义,比如温度改变0.5℃以上才算改变等……
notifyObservers方法的实现:
</span><span style="color: #0000ff;">synchronized</span> (<span style="color: #0000ff;">this</span><span style="color: #000000;">) {
</span><span style="color: #0000ff;">if</span> (!<span style="color: #000000;">changed)
</span><span style="color: #0000ff;">return</span><span style="color: #000000;">;
arrLocal </span>=<span style="color: #000000;"> obs.toArray();
clearChanged();
}
</span><span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = arrLocal.length-1; i>=0; i--<span style="color: #000000;">)
((Observer)arrLocal[i]).update(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">,arg);
}</span></pre>
布告板的实现:
<span style="color: #0000ff;">public <span style="color: #0000ff;">class CurrentConditionsDisplay <span style="color: #0000ff;">implements<span style="color: #000000;"> Observer,DisplayElement {
</span><span style="color: #0000ff;">private</span><span style="color: #000000;"> Observable observable;
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">float</span><span style="color: #000000;"> temperature;
</span><span style="color: #0000ff;">private</span> <span style="color: #0000ff;">float</span><span style="color: #000000;"> humidity;
</span><span style="color: #0000ff;">public</span><span style="color: #000000;"> CurrentConditionsDisplay(Observable observable) {
</span><span style="color: #0000ff;">this</span>.observable =<span style="color: #000000;"> observable;
observable.addObserver(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">);
}
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> display() {
System.out.println(</span>"Current conditions: " + temperature +
"F degrees and " + humidity + "% humdity."<span style="color: #000000;">);
}
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> update(Observable obs,Object arg) {
</span><span style="color: #0000ff;">if</span> (obs <span style="color: #0000ff;">instanceof</span><span style="color: #000000;"> WeatherData) {
WeatherData weatherData </span>=<span style="color: #000000;"> (WeatherData) obs;
</span><span style="color: #0000ff;">this</span>.temperature =<span style="color: #000000;"> weatherData.getTemperature();
</span><span style="color: #0000ff;">this</span>.humidity =<span style="color: #000000;"> weatherData.getHumidity();
display();
}
}
}
这里的update()方法和上面的实现不同,上面的实现是push的方法,也就是主题把数据直接送到观察者那里。而这里的是观察者通过getter()方法来自己pull所需要的数据。
但是java.util.Observable和java.util.Observer都是类而不是接口,所以要继承它们就无法继承别的类了。
JDK中的观察者模式
Swing中的JButton就用到了观察者模式。
<span style="color: #0000ff;">import<span style="color: #000000;"> javax.swing.JButton;
<span style="color: #0000ff;">import<span style="color: #000000;"> javax.swing.JFrame; <span style="color: #0000ff;">public <span style="color: #0000ff;">class<span style="color: #000000;"> SwingObserverExample {
JFrame frame;
原文链接:https://www.f2er.com/javaschema/69832.html<span style="color: #0000ff;">import<span style="color: #000000;"> javax.swing.JFrame; <span style="color: #0000ff;">public <span style="color: #0000ff;">class<span style="color: #000000;"> SwingObserverExample {
JFrame frame;
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) {
SwingObserverExample example </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> SwingObserverExample();
example.go();
}
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> go() {
frame </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> JFrame();
JButton button </span>= <span style="color: #0000ff;">new</span> JButton("Should I do it?"<span style="color: #000000;">);
button.addActionListener(</span><span style="color: #0000ff;">new</span><span style="color: #000000;"> AngelListener());
button.addActionListener(</span><span style="color: #0000ff;">new</span><span style="color: #000000;"> DevilListener());
frame.getContentPane().add(BorderLayout.CENTER,button);
frame.setSize(</span>200,200<span style="color: #000000;">);
frame.setVisible(</span><span style="color: #0000ff;">true</span><span style="color: #000000;">);
}
</span><span style="color: #008000;">//</span><span style="color: #008000;"> 一个ActionListener就是一个观察者</span>
<span style="color: #0000ff;">class</span> AngelListener <span style="color: #0000ff;">implements</span><span style="color: #000000;"> ActionListener {
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> actionPerformed(ActionEvent e) {
System.out.println(</span>"Dont do it,you might regret it!"<span style="color: #000000;">);
}
}
</span><span style="color: #0000ff;">class</span> DevilListener <span style="color: #0000ff;">implements</span><span style="color: #000000;"> ActionListener {
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> actionPerformed(ActionEvent e) {
System.out.println(</span>"Come on,do it!"<span style="color: #000000;">);
}
}
}