设计模式学习笔记(一):观察者模式

137 阅读3分钟

定义了对象之间的一对多依赖,当一个对象改变状态时,他的所有依赖者都会收到通知并自动更新。

应用场景:气象站将测量到的温度,气压,湿度信息发送给需要的第三方,每当气象站数据变化时,依赖于它的第三方软件的数据同步变化。

不使用观察者模式时的实现方法

WeatherData类

public class WeatherData {
	private double temperature;//温度
	private double pressure;//气压
	private double humidity;//湿度
	private Client1 client1;//某第三方软件

	public WeatherData(Client1 client1) {
		this.client1 = client1;
	}
        //数据改变,为温度、气压、湿度赋值
	public void setData(double temperature,double pressure,double humidity){
		this.temperature = temperature;
		this.pressure = pressure;
		this.humidity = humidity;
		dataChange();
	}
	//数据改变时通知此气象站的依赖者
	public void dataChange(){
		client1.update(temperature, pressure, humidity);
	}
}

Client1类

public class Client1 {
	private double temperature;
	private double pressure;
	private double humidity;
        //更新数据
	public void update(double temperature, double pressure, double humidity) {
		this.temperature = temperature;
		this.pressure = pressure;
		this.humidity = humidity;
		display();
	}
	//显示数据
	public void display(){
		Date date = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		String dateStr = sdf.format(date);
		System.out.println("***client1 "+dateStr+" temperature:"+temperature);
		System.out.println("***client1 "+dateStr+" pressure:"+pressure);
		System.out.println("***client1 "+dateStr+" humidity:"+humidity);
	}
}

测试类

public class Test {

	public static void main(String[] args) {
		Client1 c1 = new Client1();
		WeatherData weatherData = new WeatherData(c1);
		weatherData.setData(10, 20, 30);
	}

}

输出结果

可以看到,当气象站数据变化时,实时向依赖气象站的第三方软件推送了数据。但这种方式有一个缺点,第三方软件与气象站关系是写在气象站的代码中的,如果此软件不想再接收到气象站的数据或者又有其他新的第三方软件想要订阅气象站的话就要修改气象站的代码,这不符合开闭原则,那么有没有什么更好的方法呢?

使用观察者模式

Subject接口

public interface Subject {
	void registerObject(Observer observer);

	void remove(Observer observer);

	void notifyObserver();
}

Observer接口

public interface Observer {

	void update(double tempreature,double pressure,double humidity);

}

WeatherData类

public class WeatherData implements Subject {

	private ArrayList<Observer> observers = new ArrayList<>();
	private double temperature;
	private double pressure;
	private double humidity;

	@Override
	public void registerObject(Observer observer) {
	    if(!observers.contains(observer)){
		   observers.add(observer);
	    }
	}

	@Override
	public void remove(Observer observer) {
		observers.remove(observer);
	}

	@Override
	public void notifyObserver() {
		for(Observer observer:observers){
			observer.update(temperature,pressure,humidity);
		}
	}

	public void setData(double temperature,double pressure,double humidity){
		this.temperature = temperature;
		this.pressure = pressure;
		this.humidity = humidity;
		dataChange();
	}

	public void dataChange(){
		notifyObserver();
	}
}

Client1类

public class Client1 implements Observer {

	private double temperature;
	private double pressure;
	private double humidity;

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

	public void display(){
		Date date = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		String dateStr = sdf.format(date);
		System.out.println("***client1 "+dateStr+" temperature:"+temperature);
		System.out.println("***client1 "+dateStr+" pressure:"+pressure);
		System.out.println("***client1 "+dateStr+" humidity:"+humidity);
	}

}

测试类

public class Test {

	public static void main(String[] args) {
		WeatherData weatherData = new WeatherData();
		Client1 c1 = new Client1();
		weatherData.registerObject(c1);
		weatherData.setData(10, 20, 30);

	}

}

测试结果

可以看到我们使用观察者模式成功实现了需求,而且使用观察者模式可以随意增加或减少对气象站的依赖者而不必修改气象站的源码。但其实我们在实际开发中是不需要自定义主题和观察者接口的,因为java提供了对观察者模式的支持。

使用Java内置的观察者模式

Java中提供了Observable类和Observer接口,被观察的主题需要继承Observable类,观察者需要实现Observer接口,使用java内置的类完成需求代码如下。

WeatherData类

public class WeatherData extends Observable{
	private double temperature;
	private double pressure;
	private double humidity;

	public double getTemperature() {
		return temperature;
	}
	public double getPressure() {
		return pressure;
	}
	public double getHumidity() {
		return humidity;
	}

	public void setData(double temperature,double pressure,double humidity){
		this.temperature = temperature;
		this.pressure = pressure;
		this.humidity = humidity;
		setChanged();
		notifyObservers();
	}

}

Client1类

public class Client1 implements Observer{

	private double temperature;
	private double pressure;
	private double humidity;

	@Override
	public void update(Observable o, Object arg) {
		if(o instanceof WeatherData){
			WeatherData weatherData = (WeatherData) o;
			this.temperature = weatherData.getTemperature();
			this.pressure = weatherData.getPressure();
			this.humidity = weatherData.getHumidity();
			display();
		}
	}

	public void display(){
		Date date = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		String dateStr = sdf.format(date);
		System.out.println("***client1 "+dateStr+" temperature:"+temperature);
		System.out.println("***client1 "+dateStr+" pressure:"+pressure);
		System.out.println("***client1 "+dateStr+" humidity:"+humidity);
	}

}

测试类

public class Test {

	public static void main(String[] args) {
		Client1 c1 = new Client1();
		WeatherData weatherData = new WeatherData();
		weatherData.addObserver(c1);

		weatherData.setData(10, 20, 30);
	}

}

输出结果

在使用java提供的Observable类时有一个注意点,在Observable类中有一个changed属性用来记录数据是否已经变化,当调用它的notifyObservers方法时,只有在changed数据为true时才会通知观察者,所以在调用notifyObservers方法之前要先调用一下setChanged方法将changed设为true(这样做是为了避免主题对数据过于敏感,导致频繁向观察者发送消息的情况,如此例的气象站温度数据变化很频繁,其实不需要每次微小变动都通知观察者,可以在变化温度查过一度时再通知观察者)。

notifyObservers方法部分代码如下图