设计模式-观察者模式

设计原则:实现对象之间的松耦合,交互对象彼此无需知道彼此实现细节。能应对变化,因为彼此的依赖降低到了最低。


另一个例子:订报。有两个角色,报社和订户。报社为主题对象,而订户为观察者。当报社有新的报纸产出(数据变化),会发送消息给相应的订户。如果订户接收,则接收报社的投递。如果订户不接受,则不接受报社的投递。

松耦合:两个对象可以相互交互,但是不清楚彼此的实现细节。

观察者模式:主体只知道观察者实现了一个接口observer。不知道观察者是谁,内部细节。?主体唯一依赖的东西是实现接口的对象列表?。所以可以在任意时候取代,删除,创建观察者。新的观察者的出现无需通知主体。主体不在乎别的,会给所有实现了该接口的观察者发送消息。我们可以独立的复用观察者和主体,因为它们是耦合的。


问题引出:有个气象站,其硬件设备可以测量压强,气温,湿度。这些数据可计算得到目前天气,预测天气,统计天气三个产出。当压强气温湿度数据发生变化后,产出数据会立即变化。产出可以得到拓展,即存在更多形式的产出类型。

原始解决方案:

先定义天气信息类,注意引入了一个产出牌的类:

public class WeatherData {
    private float data1;
    private float data2;
    private float data3;
    private CurrentWeatherCondition c;
    public WeatherData(CurrentWeatherCondition c){
        this.c=c;
    }

    public void dataUpdate(float data1,float data2,float data3){
        this.data1=data1;this.data2=data2;this.data3=data3;
        c.update(data1,data2,data3);
    }
//省略了setter/getter方法
}

产出类:

public class CurrentWeatherCondition {
    public void update(float data1,float data2,float data3){
        display();
        System.out.print(123);
    }
    public void display(){
    }
}
测试:
public class UserInterface {
    public static void main(String args[]) {
        CurrentWeatherCondition c = new CurrentWeatherCondition();
        WeatherData weatherData = new WeatherData(c);
        weatherData.dataUpdate(1,2,3);
    }
}
显然是可以通过测试的。然而这种方式类的耦合程度太高。在运用中,当需要使用到新的产出类型的时候相当麻烦的大懂干戈。可扩展性。

使用观察者模式来解决这个问题:

观察者模式:定义了对象间的一对多关系。当一个对象的状态发生改变的时候,所有的依赖者收到通知并自动更新。

WeatherData为主体,观察者为布告。布告必须注册,主体获知布告的存在后,调用布告的方法update()告知数据变化。布告板有差异,共同去实现一个接口好让主体感知。

有一个重要的事情要额外注意的是,我们无法获知布告板的数目。

首先定义三个接口:分别为主体,观察者,最后为观察者需要处理的一个display(不作为观察者模式的重点)

public interface Subject {
    public void registerObserver(Observer observer);
    public void removeObserver(Observer observer);
    public void notifyObservers();
}
public interface Observer {
    public void update(float data1,float data2,float data3);
}
public interface DisplayElement {
    public void display();
}
主体类:

public class WeatherData implements Subject {
    //情理之中又意料之外的使用了list
    private ArrayList observers;
    private float data1;
    private float data2;
    private float data3;
    public WeatherData(){
        observers=new ArrayList();
    }
    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }
    @Override
    public void removeObserver(Observer observer) {
        //注意这种写法
        int i=observers.indexOf(observer);
        if (i>=0){
            observers.remove(i);
        }
    }
    @Override
    public void notifyObservers() {
        for (int i=0;i<observers.size();i++){
            //注意这种写法
            Observer o=(Observer) observers.get(i);
            //此处是重点,多态
            o.update(data1,data2,data3);
        }
    }
    public void dataChanged(){
        notifyObservers();
    }
    public void setData(float data1,float data2,float data3){
        this.data1=data1;this.data2=data2;this.data3=data3;
        dataChanged();
    }
}
对于观察者:

public class CurrentConditionDisplay implements Observer,DisplayElement {
    private float data1;
    private float data2;
    //之所以保留subject的引用是为了当出现需要取消注销的时候方便
    private Subject weatherData;
    //注意此处的逻辑,我们是后期开发多的一端
    public CurrentConditionDisplay(Subject weatherData){
        this.weatherData=weatherData;
        //重点
        weatherData.registerObserver(this);
    }
    @Override
    public void display() {
        System.out.println("helloworld!");
    }
    @Override
    public void update(float data1, float data2, float data3) {
        this.data1=data1;
        this.data2=data2;
        display();
    }
}
测试:

public class UserInterface {
    public static void main(String args[]){
        WeatherData weatherData=new WeatherData();
        CurrentConditionDisplay currentConditionDisplay=new CurrentConditionDisplay(weatherData);
        weatherData.setData(1,1,1);
    }
}

实际上Java内置观察者模式。java.util包中包含最基本的observer和observable(可观察者)类。我们甚至可以只用拉,推的方式获取数据。有了内置的帮助,我们实现了observable后,告诉它如何通知观察者即可。

Java内置的观察者模式:observable是类,不是接口。

如何把对象变成可观察者:实现观察者接口,调用注册方法成为观察者,也可remove。

可观察者如何送出通知:继承observable类产生可观察类,调用setChanged()方法声明状态改变,调用notifyObservers()方法或notifyObservers(Object arg)方法。

观察者如何收到消息:观察者实现了更新方法update(Observable o,Object arg)

推、拉两种方式实现信息交互:

若想推push数据给观察者,则使用notifyObservers(Object arg),把数据传输。

若观察者想拉pull数据:

可观察者类:注意继承类,我们无需注册和删除观察者部分,直接被继承。可观察者无需建立数据结构存储观察者。

import java.util.Observable;
public class WeatherData extends Observable {
	private float temperature;
	private float humidity;
	private float pressure;
	public WeatherData() { }
	public void measurementsChanged() {
		setChanged();//注意没有提供数据进来,所以是拉的方式而不是推。
		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 getHumidity() {
		return humidity;
	}
	public float getPressure() {
		return pressure;
	}
}

观察者:注意update方法。

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 WeatherStationObservable.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");
	}
}

测试:

public class UserInterface {

	public static void main(String[] args) {
		WeatherData weatherData = new WeatherData();
		CurrentConditionsDisplay currentConditions = new CurrentConditionsDisplay(weatherData);

		weatherData.setMeasurements(80, 65, 30.4f);
		weatherData.setMeasurements(82, 70, 29.2f);
		weatherData.setMeasurements(78, 90, 29.2f);
	}
}



观察者模式定义了对象的一对多的关系。可观察者用一个共同的接口更新观察者。观察者和可观察者松耦合的关系,可观察者不知道观察者的细节,只知道实现了某接口。

可以使用推拉两种方式实现数据交互,推的方式更适合。有多个观察者的时候,若使用了Java内置方法,无法保证顺序。


注意:Java内置的观察者相关接口和类问题:可观察者需要继承一个类,架构不太合适(少继承多组合,Java仅支持单继承),可观察者维护的观察者列表在notifyObservers处顺序会出现乱。实现自身的观察者模式效果更好。





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值