设计模式之观察者模式Oberver Pattern-Java版

一、观察者模式

《Head First Design Patterns》:The Observer Pattern defines a one-to-many
dependency between objects so that when one
object changes state, all of its dependents are
notified and updated automatically.
即:观察者模式在对象之间定义了一对多的依赖,当一个对象状态改变了,它的全部依赖都会自动被通知和更新。
简单说,就是一个对象状态改变后,它所依赖的对象全部要更新。

类图:

二、观察者模式案例

一家天气预报公司,会记录三种天气状态:气温、湿度、气压,当监察到状态改变时(忽略几秒钟还是几分钟这些细节),会在三台显示器上将最新天气情况显示出来,分别是显示气温、湿度和气压,显示最高气温、平均气温和最低气温,显示天气预报(下雨、下雪、晴天等)。

版本一:只有一个WeatherData类,获取到最新温度、湿度、气压时,会自动调用measurementChanged方法。

public class WeatherData {
	public void measurementsChanged() {
		//假定getTemperature、getHumidity、getPressure可以获取最新状态
		double temp=getTemperature();
		double humidity=getHumidity();
		double pressure=getPressure();
		//第一台显示
		currentConditionDisplay.update(temp,humidity,pressure);
		//第二台显示
		statisticsDisplay.update(temp,humidity,pressure);
		//第三台显示
		forecastDisplay.update(temp,humidity,pressure);
	}
}

分析:温度、湿度、气压应该封装为成员变量,三条update方法直接通过调用来编写,就等于是面向实现编程而非面向接口编程了。


版本二:采用观察者模式来解决,创建观察者接口,同时有多个观察者实现类,创建主题类接口,主题实现类(WeatherData)持有观察者接口类型的集合,通过集合,可以增到依赖对象中,也可以移除对象。

//主题类接口,包括注册新增观察者、移除观察者、通知观察者方法
interface Subject {
	public void registerObserver(Observer o);
	public void removeObserver(Observer o);
	public void notifyObservers();
}

//具体的主题类
public class WeatherData implements Subject {
	private double temperature,hummidity, pressure;
	private List<Observer> list=new ArrayList<>();
	
	public void measurementsChanged() {
		//假定以下三行是最新测量出来的数据
		temperature=temperature+1;
		hummidity=hummidity-1;
		pressure=pressure+1;
		notifyObservers();//通知观察者
	}
	
	//新增观察者
	@Override
	public void registerObserver(Observer o) {
		list.add(o);
	}
	//删除观察者
	@Override
	public void removeObserver(Observer o) {
		list.remove(o);
	}

	//发生改变后,通知观察者
	@Override
	public void notifyObservers() {
		for(Observer o: list) {
			o.update(temperature, hummidity, pressure);
		}
	}
	
	public static void main(String[] args) {
		WeatherData weather=new WeatherData();
		weather.measurementsChanged();
		Observer display1 =new CurrentConditionDiplay(weather);
		Observer display2 =new WeatherStatusDiplay(weather);
		Observer display3 =new ForecastDisplay(weather);
		weather.measurementsChanged();
		weather.removeObserver(display2);//取消display2
		System.out.println("==取消2号显示器后==");
		weather.measurementsChanged();
	}
}


//显示信息的接口,这个不是重点
interface Displayment {
	public void display();
}

//观察者接口
interface Observer {
	public void update(double temperature, double hummidity, double pressure);
}

//具体观察者1:当前天气情况
class CurrentConditionDiplay implements Displayment, Observer {
	private Subject weather;
	private double temperature,humidity,pressure;
	
	public CurrentConditionDiplay(Subject weather) {
		this.weather=weather;
		weather.registerObserver(this);//自动订阅
	}

	@Override
	public void update(double temperature, double hummidity, double pressure) {
		this.temperature=temperature;
		this.humidity=hummidity;
		this.pressure=pressure;
		display();
	}

	@Override
	public void display() {
		System.out.println("Current Conditions:");
		System.out.println("Temp:"+temperature+"°");
		System.out.println("Humidity:"+humidity);
		System.out.println("Pressure:"+pressure+"hPa");
		
	}
}
//具体观察者2:天气状态显示
class WeatherStatusDiplay implements Displayment, Observer {
	
	private Subject weather;
	private double temperature;
	
	public WeatherStatusDiplay(Subject weather) {
		this.weather=weather;
		weather.registerObserver(this);
	}

	@Override
	public void update(double temperature, double hummidity, double pressure) {
		this.temperature=temperature;
		display();
	}

	@Override
	public void display() {
		System.out.println("Weather Status:");
		System.out.println("Avg.temp: "+temperature+"°");
		System.out.println("Min.temp: " +temperature+"°");
		System.out.println("Max.temp: "+temperature+"°");
	}
}

//具体观察者3:预测天气
class ForecastDisplay implements Displayment, Observer {
	private Subject weather;
	private double temperature,hummidity,pressure;
	
	public ForecastDisplay(Subject weather) {
		this.weather=weather;
		weather.registerObserver(this);
	}
	@Override
	public void update(double temperature, double hummidity, double pressure) {
		this.temperature=temperature;
		this.hummidity=hummidity;
		this.pressure=pressure;
		display();
	}
	@Override
	public void display() {
		if(temperature<=0) {
			System.out.println("下雪");
		} else if(hummidity<=20&&pressure>=100) {
			System.out.println("下雨");
		} else {
			System.out.println("晴天");
		}
	}
}

类图:

 

分析:

  1. 写好观察者接口,具体观察者实现该接口,即面向接口编程。
  2. 编写主题接口,具体主题实现该接口,同时持有观察者接口类型集合的引用,可以加入新的观察者,也可以移除观察者。
  3. 在具体观察者类中,持有主题类接口类型的引用,在构造方法中,传入参数也是主题类接口类型,同时通过主题类将当前类加入到观察者集合中。
  4. 当主题类状态发生变化时,遍历观察者集合,调用观察者的方法。

版本三:通过JavaAPI提供的观察者接口(java.util.Observer)和主题类(java.util.Observable)来实现

java.util.Observer观察者接口:

public interface Observer {
    /**
    * o  主题对象
    * arg 参数
    */
    void update(Observable o, Object arg);
}

java.util.Observable类:

public class Observable {
    private boolean changed = false;//标志是否发生改变
    private Vector<Observer> obs;

    public Observable() {
        obs = new Vector<>();
    }
    //增加观察者
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }
    //移除观察者
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }
    //通知观察者
    public void notifyObservers(Object arg) {
        Object[] arrLocal;
        synchronized (this) {
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }
    
    protected synchronized void setChanged() {
        changed = true;
    }
    
    protected synchronized void clearChanged() {
        changed = false;
    }
    

 具体代码:

//具体主题类,直接继承java.util.Observable类
public class WeatherData extends Observable {
	
	private double temperature, humidity, pressure;
	
	public WeatherData() {
	}
	
	public void measurementsChanged() {
		setChanged();
		notifyObservers();
	}
	
	public void setMeasurements(double t, double h, double p) {
		temperature=t;
		humidity=h;
		pressure=p;
		measurementsChanged();
	}
	
	public double getTemperature() {
		return temperature;
	}

	public double getHumidity() {
		return humidity;
	}

	public double getPressure() {
		return pressure;
	}
	
	public static void main(String[] args) {
		WeatherData weather=new WeatherData();
		Observer display1 =new CurrentConditionObserver(weather);
		Observer display2 =new WeatherStatusObserver(weather);
		Observer display3 =new ForecastDisplay(weather);
		weather.setMeasurements(30, 60, 50);
		weather.deleteObserver(display1);
		weather.setMeasurements(24, 70, 20);
	}
}

interface DisplayElement {
	void display();
}
//实现java.util.Observer观察者接口和DisplayElement显示接口
class CurrentConditionObserver implements Observer, DisplayElement {
	private Observable observable;
	private double temperature;
	private double humidity;
	public CurrentConditionObserver(Observable observable) {
		this.observable=observable;
		observable.addObserver(this);
	}

	@Override
	public void update(Observable o, Object arg) {
		if(o instanceof WeatherData) {
			WeatherData w=(WeatherData) o;
			temperature=w.getTemperature();
			humidity=w.getHumidity();
			display();
		}
	}
	
	@Override
	public void display() {
		System.out.println("Current conditions: "+temperature
				+"F degrees and "+humidity+"% humidity");
	}
}

class WeatherStatusObserver  implements Observer, DisplayElement {
	private Observable observable;
	private double temperature;
	public WeatherStatusObserver(Observable observable) {
		this.observable=observable;
		observable.addObserver(this);
	}

	@Override
	public void update(Observable o, Object arg) {
		if(o instanceof WeatherData) {
			WeatherData w=(WeatherData) o;
			temperature=w.getTemperature();
			display();
		}
	}

	@Override
	public void display() {
		System.out.println("Weather Status:");
		System.out.println("Avg.temp: "+temperature+"°");
		System.out.println("Min.temp: " +temperature+"°");
		System.out.println("Max.temp: "+temperature+"°");
		
	}
	
}

class ForecastDisplay implements Observer {
	private Observable observable;
	private double temperature, humidity, pressure;
	
	public ForecastDisplay(Observable observable) {
		this.observable=observable;
		observable.addObserver(this);
	}

	@Override
	public void update(Observable o, Object arg) {
		if(o instanceof WeatherData) {
			WeatherData weatherData=(WeatherData) o;
			this.temperature=weatherData.getTemperature();
			this.humidity=weatherData.getHumidity();
			this.pressure=weatherData.getPressure();
			display();
		}
	}
	
	public void display() {
		if(temperature<=0) {
			System.out.println("下雪");
		} else if(humidity<=20&&pressure>=100) {
			System.out.println("下雨");
		} else {
			System.out.println("晴天");
		}
	}
}

分析

  1. 注意到Obserable主题类是一个class而不是interface,违背了面向接口编程的原则;
  2. 倘若使用组合来代替继承,而clearChanged和setChanged方法都是protected修饰,意味着只能同包或者子类可以访问该方法,组合除非在同一包下,否则不能使用,违背了组合优于继承。因此,如果有必要的话,可以自行实现一个带有接口的Observable类。

 

三、观察者模式应用场景

订阅服务,如订阅报纸、订阅电台、订阅图书、订阅天气预报......

Java Swing中的监听机制就是典型的观察者模式例子,某个组件可以通过addActionListener增加多个观察者,当事件触发时,所有的监听类的监听方法actionPerformed都会执行。

浅谈观察者模式中使用到的面向对象设计原则:

  1. 分离变化、封装变化:观察者是容易变化的,因此要分离出来,封装成观察者类。
  2. 面向接口编程:将所有观察者类继承唯一一个观察者接口,在主题类中的List中使用接口类型,这样观察者类的变化不会引起主题类的变化,因为接口是稳定的,而具体的类是不稳定的(容易改变)。
  3. 组合优于继承:主题类中使用了List<Observer>,意味着使用了一对多的组合。为什么组合优于继承?首先,Java只支持单继承,继承是稀缺的,不要轻易使用;其次,组合比继承有更好的可测试性,继承需要测试父类和派生类,比较麻烦;再次,组合比继承更加灵活,在设计模式的策略模式和装饰模式中,已经有了很好的证明。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学无止境jl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值