这是我看Head first设计模式书籍之后想要总结的知识点,一方面是对自己学习的东西总结和提炼加强自己的理解和记忆,另一方面是给大家简化这本书,方便大家快速了解各种设计模式。
我想提醒大家的是,设计模式只是前人总结的一些经验套路,实际上还是要在开发项目中慢慢体会,不可成为设计模式的中毒患者,强行照搬设计模式的一些规则。
我们这次要讲解的设计模式是观察者模式
我们举个栗子:
比如说我们要设计一个气象站应用,由WeatherData对象负责追踪目前的天气状况(温度、湿度、气压),我们有三个布告板,分别显示当前的情况,气象统计及简单预报。当WeatherDataObject对象获得最新的测量数据时,三种布告板必须实时更新。
WeatherData中提供了getTemperature()、getHumidity()、getPressure()、measurementsChanged()、measurementsChanged()等方法,另外一旦气象测量更新,measurementsChanged()方法会被调用。我们的任务是实现measurementsChanged(),好让它更新目前状况,气象统计,天气预报的显示布告板。
第一种思路(也是错误的思路):
public class WeatherData{
//实例变量声明
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
currentConditionsDisplay.update(temp. humidity, pressure);
statisticsDisplay.update(temp, humidity, pressure);
forecastDisplay.update(temp, humidity, pressure);
}
这样做带来的问题:
1.针对具体实现编程,而非针对接口。这样我们增加或删除布告板时必须修改WeatherData类中的代码
2.要将改变的地方封装起来,这样耦合性太高了。
3.无法在运行时动态地增加或删除布告板
下面就是我们的观察者模式出场了!
观察者模式:
定义了对象之间的一对多依赖,这样依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
实现观察者模式的方法不只一种,但是以包含Subject和Observer接口的类设计的做法最常见。
主题接口Subject :
包含方法:抽象方法registerObserver() removeObserver() notifyObservers()
作用:对象使用此接口注册为观察者,或者把自己从观察中删除 当主题状态发生更新时,通知观察者比起让许多对象控制同一份数据来,可以得到更干净的OO设计。
ConcreteSubject:
包含方法: 实现Subject中的方法以及一些其他方法
作用: 多个主题的具体类
观察者接口Observer:
包含方法: update()
作用: 多个观察者可以实现这个接口,通过当Subject实现类状态改变时可以通过update()调用来接受通知和数据
这样做的好处:
松耦合 当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。观察者模式提供了一种对象设计
,让主题和观察者之间松耦合,能够建立有弹性的系统,应对变化,这是因为对象之间的互相依赖降到了最低。
这里依赖就只有主题中实现Observer接口的对象列表。
主题: 只知道观察者实现了Observer接口,有个update()方法可以让我通知观察者
直接看代码部分:
建立主题Subject、观察者Observer、DisplayElement(三个公告布实现这个接口,这里我们就实现一个公告布类)接口
public interface Subject{
//这两个方法都需要一个观察者作为变量,该观察者是用来注册或删除的
public void registerObserver(Observer o);
public void removeObserver(Observer o);
//当主题状态改变时,这个方法会被调用,以通知所有的观察者
public void notifyObservers();
}
public interface Observer{
//当状态改变时,主题会把这些状态值当做方法的参数,传送给观察者
public void update(float temp, float humidity, float pressure);
}
public interface DisplayElement{
public void display();
}
WeatherData类实现主题Subject接口
public class WeatherData implements Subject{
private ArrayList observers; //用于记录观察者
private float temperature;
private float pressure;
private float humidity;
public WeatherData(){
observers = new ArrayList();
}
public void registerObserver(Observer o){
observers.add(o);
}
public void removeObserver(Observer o){
int i = observers.indexOf(o);
if(i >= 0){
observers.remove(i);
}
}
public void notifyObservers(){
for(int i = 0; i < observers.size(); i++){
Observer observer = (Observer) observers.get(i);
observer.update(temperature, humidity, pressure);
}
}
//当状态改变时,会自动回调这个方法
public vooid measurementsChanged(){
notifyObservers();
}
public void setMeasurements(float temperature, float humidity, float pressure){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
//其他方法
}
布告板(实现了观察者Observer接口)
public class CurrentConditionsDisplay implements Observer, DisplayElement{
private float temperature;
private float humidity;
private Subject weatherData;
//构造器需要weatherData对象作为注册之用
public CurrentConditionsDisplay(Subject weatherData){
this.weatherData = weatherData;
weatherData.registerObserver();
}
public void update(float temperature, float humidity, float pressure){
this.temperature = temperature;
this.humidity = humidity;
display();
}
public void display(){
System.out.println("Current conditions: " + temperature
+ "F degrees and " + humidity + "% humidity");
}
}
测试程序
public class WeatherStation{
public static void main(String args[]){
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
weatherData.setMeasurements(80, 65, 30.4f);
}
}
我们从无到有实现了观察者模式,但是Java API有内置的观察者模式java.util包内包含基本的Observer接口(观察者接口)和Observable(可观察者类)
注意Observable是一个主题类,有addObserver()和deleteObserver()以及notifyObservers()的具体实现方法,也有一个setChanged()方法
我们让WeatherData类继承这个Observable类,观察者CurrentConditionsDisplay实现Observer接口
我们使用java的内置API来演示一遍
WeatherData
import java.util.Observable;
import java.util.Observer;
public class WeatherData extends Observable{
//private ArrayList observers;
/*不需要记录观察者,注册,添加和通知代码,父类已经替我们做好了*/
private float temperature;
private float pressure;
private float humidity;
public WeatherData(){}
public void measurementsChanged(){
setChanged(); //这个方法setChanged()可以让changed变量为true,可以通知观察者,否则就不能通知。因为我们不能状态变化一点就通知观察者,要有个限制
notifyObservers();
}
public void setMeasurements(float temperature, float humidity, float pressure){
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
public float getTemperature(){
return temperature;
}
public float getPressure(){
return pressure;
}
public float getHumidity(){
return humidity();
}
}
CurrentConditionsDisplay观察者类
import java.util.Observable;
import java.util.Observer;
public class CurrentConditionsDisplay implements Observer, DisplayElement{
Observable observable;
private float temperature;
private float humidity;
public CurrentConditionsDisplay(Observable observable){
this.observable = observable;
observable.addObserver(this);
}
public void update(Observable obs, Object arg){
if(obs instanceof WeatherData){
WeatherData weatherData = (WeatherData) obs;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
}
public void display(){
System.out.println("Current conditions: " + temperature
+ "F degrees and " + humidity + "% humidity");
}
}
java.util.Observable的缺点:
Observable不是接口,而是一个类。这样限制了它的使用和复用。如果某类想同时具有Observable类和另一个超类的行为,就会陷入两难,毕竟java不支持多重继承。这限制了Observable的复用潜力。
相信很多人都写过小游戏吧或者GUI程序,如Android开发中控件、View、ViewGroup注册监听器还有Java Swing库中注册监听事件,都使用了观察者模式。
例子:
public class SwingObserverExample{
JFrame frame;
public static void main(String rags[]){
SwingObserverExample example = new SwingObserverExample();
example.go();
}
public void go(){
frame = new JFrame();
JButton button = new JButton("Should I do it?");
button.addActionListener(new AngelListener());
button.addActionListener(new DevilListener());
frame.getContentPane().add(BorderLayout.CENTER, button);
//....省略
}
class AngelListener implements ActionListener{
public void actionPerformed(ActionEvent event){
System.out.println("Don't do it, you might regred it!");
}
}
class DevilListener implements ActionListener{
public void actionPerformed(ActionEvent event){
System.out.println("Come on, do it!");
}
}
}
我们定义的内部类AngulListener和DevilListener都是观察者,按钮就是主题/可观察者,按钮状态改变时(isChecked()),这样就会通知观察者执行观察者中的逻辑。
以后会总结观察者模式的代表人物--MVC模式