设计模式之观察者模式

情景:

气象站会实时发布气象数据,要求创建布告板,并把气象站发布的数据显示出来。

布告板会有很多,随时回添加一个或删除一个,而每个布告板显示的格式也不尽相同。

观察者模式:定义了对象之间的一对多的依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

被观察对象称作主题(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;"&gt;private</span> ArrayList<Observer><span style="color: #000000;"&gt; observers;
</span><span style="color: #0000ff;"&gt;private</span> <span style="color: #0000ff;"&gt;float</span><span style="color: #000000;"&gt; temperature;
</span><span style="color: #0000ff;"&gt;private</span> <span style="color: #0000ff;"&gt;float</span><span style="color: #000000;"&gt; humidity;
</span><span style="color: #0000ff;"&gt;private</span> <span style="color: #0000ff;"&gt;float</span><span style="color: #000000;"&gt; pressure;

</span><span style="color: #0000ff;"&gt;public</span><span style="color: #000000;"&gt; WeatherData() {
    observers </span>= <span style="color: #0000ff;"&gt;new</span> ArrayList<><span style="color: #000000;"&gt;();
}

@Override
</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; registerObserver(Observer o) {
    observers.add(o);
}

@Override
</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; removeObserver(Observer o) {
    </span><span style="color: #0000ff;"&gt;int</span> i =<span style="color: #000000;"&gt; observers.indexOf(o);
    </span><span style="color: #0000ff;"&gt;if</span> (i >= 0<span style="color: #000000;"&gt;) {
        observers.remove(i);
    }
}

@Override
</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; notifyObservers() {
    </span><span style="color: #0000ff;"&gt;for</span> (<span style="color: #0000ff;"&gt;int</span> i = 0; i < observers.size(); i++<span style="color: #000000;"&gt;) {
        Observer observer </span>=<span style="color: #000000;"&gt; observers.get(i);
        observer.update(temperature,humidity,pressure);
    }
}

</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; measurementsChanged() {
    notifyObservers();
}

</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span> setMeasurements(<span style="color: #0000ff;"&gt;float</span> temperature,<span style="color: #0000ff;"&gt;float</span><span style="color: #000000;"&gt; pressure) {
    </span><span style="color: #0000ff;"&gt;this</span>.temperature =<span style="color: #000000;"&gt; temperature;
    </span><span style="color: #0000ff;"&gt;this</span>.humidity =<span style="color: #000000;"&gt; humidity;
    </span><span style="color: #0000ff;"&gt;this</span>.pressure =<span style="color: #000000;"&gt; 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><span style="color: #0000ff;"&gt;private</span> <span style="color: #0000ff;"&gt;float</span><span style="color: #000000;"&gt; temperature;
</span><span style="color: #0000ff;"&gt;private</span> <span style="color: #0000ff;"&gt;float</span><span style="color: #000000;"&gt; humidity;
</span><span style="color: #0000ff;"&gt;private</span><span style="color: #000000;"&gt; Subject weatherData;

</span><span style="color: #0000ff;"&gt;public</span><span style="color: #000000;"&gt; CurrentConditionsDisplay(Subject weatherData) {
    </span><span style="color: #0000ff;"&gt;this</span>.weatherData =<span style="color: #000000;"&gt; weatherData;
    weatherData.registerObserver(</span><span style="color: #0000ff;"&gt;this</span><span style="color: #000000;"&gt;);
}

@Override
</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; display() {
    System.out.println(</span>"Current conditions: " + temperature + 
            "F degrees and " + humidity + "% humdity."<span style="color: #000000;"&gt;);
}

@Override
</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span> update(<span style="color: #0000ff;"&gt;float</span> temperature,<span style="color: #0000ff;"&gt;float</span><span style="color: #000000;"&gt; pressure) {
    </span><span style="color: #0000ff;"&gt;this</span>.temperature =<span style="color: #000000;"&gt; temperature;
    </span><span style="color: #0000ff;"&gt;this</span>.humidity =<span style="color: #000000;"&gt; humidity;
    display();
}

}
……………………
<span style="color: #0000ff;">public <span style="color: #0000ff;">class ChineseDisplay <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;"&gt;public</span><span style="color: #000000;"&gt; ChineseDisplay(Subject data) {
    </span><span style="color: #0000ff;"&gt;this</span>.weatherData =<span style="color: #000000;"&gt; data;
    weatherData.registerObserver(</span><span style="color: #0000ff;"&gt;this</span><span style="color: #000000;"&gt;);
}

@Override
</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; display() {
    System.out.println(</span>"天气状况: 气温:" + temperature + 
            "F, 湿度:" + humidity + "%, 气压:" + pressure + " Pa。"<span style="color: #000000;"&gt;);
}

@Override
</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span> update(<span style="color: #0000ff;"&gt;float</span> temperature,<span style="color: #0000ff;"&gt;float</span><span style="color: #000000;"&gt; pressure) {
    </span><span style="color: #0000ff;"&gt;this</span>.temperature =<span style="color: #000000;"&gt; temperature;
    </span><span style="color: #0000ff;"&gt;this</span>.humidity =<span style="color: #000000;"&gt; humidity;
    </span><span style="color: #0000ff;"&gt;this</span>.pressure =<span style="color: #000000;"&gt; pressure;
    display();
}

}

测试类:

</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;static</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; main(String[] args) { WeatherData weatherData </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; WeatherData(); CurrentConditionsDisplay conditionsDisplay </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; CurrentConditionsDisplay(weatherData); StatisticsDisplay statisticsDisplay </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; StatisticsDisplay(weatherData); ChineseDisplay chineseDisplay </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; ChineseDisplay(weatherData); weatherData.setMeasurements(</span>80,65,30.4f<span style="color: #000000;"&gt;); System.out.println(); weatherData.setMeasurements(</span>82,70,29.2f<span style="color: #000000;"&gt;); System.out.println(); weatherData.setMeasurements(</span>78,90,29.2f<span style="color: #000000;"&gt;); }

}

输出:

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。

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><span style="color: #0000ff;"&gt;public</span><span style="color: #000000;"&gt; WeatherData() {}

</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; measurementsChanged() {
    setChanged();
    notifyObservers();
}

</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span> setMeasurements(<span style="color: #0000ff;"&gt;float</span> temperature,<span style="color: #0000ff;"&gt;float</span><span style="color: #000000;"&gt; pressure) {
    </span><span style="color: #0000ff;"&gt;this</span>.temperature =<span style="color: #000000;"&gt; temperature;
    </span><span style="color: #0000ff;"&gt;this</span>.humidity =<span style="color: #000000;"&gt; humidity;
    </span><span style="color: #0000ff;"&gt;this</span>.pressure =<span style="color: #000000;"&gt; pressure;
    measurementsChanged();
}

</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;float</span><span style="color: #000000;"&gt; getTemperature() {
    </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; temperature;
}

</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;float</span><span style="color: #000000;"&gt; getHumidity() {
    </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; humidity;
}

</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;float</span><span style="color: #000000;"&gt; getPressure() {
    </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt; pressure;
}

}

观察Observable源码setChanged方法

=

有时候改变需要自己定义,比如温度改变0.5℃以上才算改变等……

notifyObservers方法的实现:

</span><span style="color: #0000ff;"&gt;synchronized</span> (<span style="color: #0000ff;"&gt;this</span><span style="color: #000000;"&gt;) { </span><span style="color: #0000ff;"&gt;if</span> (!<span style="color: #000000;"&gt;changed) </span><span style="color: #0000ff;"&gt;return</span><span style="color: #000000;"&gt;; arrLocal </span>=<span style="color: #000000;"&gt; obs.toArray(); clearChanged(); } </span><span style="color: #0000ff;"&gt;for</span> (<span style="color: #0000ff;"&gt;int</span> i = arrLocal.length-1; i>=0; i--<span style="color: #000000;"&gt;) ((Observer)arrLocal[i]).update(</span><span style="color: #0000ff;"&gt;this</span><span style="color: #000000;"&gt;,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;"&gt;private</span><span style="color: #000000;"&gt; Observable observable;
</span><span style="color: #0000ff;"&gt;private</span> <span style="color: #0000ff;"&gt;float</span><span style="color: #000000;"&gt; temperature;
</span><span style="color: #0000ff;"&gt;private</span> <span style="color: #0000ff;"&gt;float</span><span style="color: #000000;"&gt; humidity;

</span><span style="color: #0000ff;"&gt;public</span><span style="color: #000000;"&gt; CurrentConditionsDisplay(Observable observable) {
    </span><span style="color: #0000ff;"&gt;this</span>.observable =<span style="color: #000000;"&gt; observable;
    observable.addObserver(</span><span style="color: #0000ff;"&gt;this</span><span style="color: #000000;"&gt;);
}

@Override
</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; display() {
    System.out.println(</span>"Current conditions: " + temperature + 
            "F degrees and " + humidity + "% humdity."<span style="color: #000000;"&gt;);
}

@Override
</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; update(Observable obs,Object arg) {
    </span><span style="color: #0000ff;"&gt;if</span> (obs <span style="color: #0000ff;"&gt;instanceof</span><span style="color: #000000;"&gt; WeatherData) {
        WeatherData weatherData </span>=<span style="color: #000000;"&gt; (WeatherData) obs;
        </span><span style="color: #0000ff;"&gt;this</span>.temperature =<span style="color: #000000;"&gt; weatherData.getTemperature();
        </span><span style="color: #0000ff;"&gt;this</span>.humidity =<span style="color: #000000;"&gt; 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;

</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;static</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; main(String[] args) {
    SwingObserverExample example </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; SwingObserverExample();
    example.go();
}

</span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; go() {
    frame </span>= <span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; JFrame();

    JButton button </span>= <span style="color: #0000ff;"&gt;new</span> JButton("Should I do it?"<span style="color: #000000;"&gt;);
    button.addActionListener(</span><span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; AngelListener());
    button.addActionListener(</span><span style="color: #0000ff;"&gt;new</span><span style="color: #000000;"&gt; DevilListener());
    frame.getContentPane().add(BorderLayout.CENTER,button);
    frame.setSize(</span>200,200<span style="color: #000000;"&gt;);
    frame.setVisible(</span><span style="color: #0000ff;"&gt;true</span><span style="color: #000000;"&gt;);
}
</span><span style="color: #008000;"&gt;//</span><span style="color: #008000;"&gt; 一个ActionListener就是一个观察者</span>
<span style="color: #0000ff;"&gt;class</span> AngelListener <span style="color: #0000ff;"&gt;implements</span><span style="color: #000000;"&gt; ActionListener {
    @Override
    </span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; actionPerformed(ActionEvent e) {
        System.out.println(</span>"Dont do it,you might regret it!"<span style="color: #000000;"&gt;);
    }
}


</span><span style="color: #0000ff;"&gt;class</span> DevilListener <span style="color: #0000ff;"&gt;implements</span><span style="color: #000000;"&gt; ActionListener {
    @Override
    </span><span style="color: #0000ff;"&gt;public</span> <span style="color: #0000ff;"&gt;void</span><span style="color: #000000;"&gt; actionPerformed(ActionEvent e) {
        System.out.println(</span>"Come on,do it!"<span style="color: #000000;"&gt;);
    }
}

}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,它是针对软件开发中经常遇到的一些设计问题,总结出来的一套通用的解决方案。
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
迭代器模式提供了一种方法,用于遍历集合对象中的元素,而又不暴露其内部的细节。
外观模式又叫门面模式,它提供了一个统一的(高层)接口,用来访问子系统中的一群接口,使得子系统更容易使用。
单例模式(Singleton Design Pattern)保证一个类只能有一个实例,并提供一个全局访问点。
组合模式可以将对象组合成树形结构来表示“整体-部分”的层次结构,使得客户可以用一致的方式处理个别对象和对象组合。
装饰者模式能够更灵活的,动态的给对象添加其它功能,而不需要修改任何现有的底层代码。
观察者模式(Observer Design Pattern)定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。
代理模式为对象提供一个代理,来控制对该对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。
工厂模式(Factory Design Pattern)可细分为三种,分别是简单工厂,工厂方法和抽象工厂,它们都是为了更好的创建对象。
状态模式允许对象在内部状态改变时,改变它的行为,对象看起来好像改变了它的类。
命令模式将请求封装为对象,能够支持请求的排队执行、记录日志、撤销等功能。
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 基本介绍 **意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为
享元模式(Flyweight Pattern)(轻量级)(共享元素)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结