zoukankan      html  css  js  c++  java
  • 学习笔记 设计模式之观察者模式

        Head First里边给出的场景是

        客户需要这样一个App:一个WheatherData类可以从气象站获得气象信息(temperature, humidity, pressure and so on).一旦这些信息改变,就会更新气象站的三个公告板(CurrentConditionBulletin, StatisticsBulletin and ForecastBulletin, 另外,用户还可以随时增加/删除/更改自己的布告板)。

        客户给出了WheatherData的类图:

        

        结合以上描述和类图我们已知:

        1. 由Getter可以或得气象信息

        2. 一旦气象信息有改变,调用measurementsChanged()方法更新所有的布告板

        3. 现在需要三个布告板

        4. Customer可以定义自己的布告板

        首先看一个不好的实现:

        

    public class WeatherData {       
    
            public void MeasurementsChanged()
            {
                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. 找出程序中变化的部分,将其与固定不变的部分隔离

        在这个场景中,变化的是布告栏的类型和数量,在这个不好的例子中,我们改变布告栏的类型和数量,势必要修改WheatherData这个类。如何才能可以随意修改布告栏的数量和类型,却不用修改WheatherData类呢?

        2. 针对接口编程,不针对实现编程

        明显,statisticsDisplay,statisticsDisplay,forecastDisplay均是具体的实现的编程,如果以后我们增加修改布告栏,都必须修改这段程序。而且三个方法( update() )的名字和参数相同,一个统一的接口可行?

        可以根据报纸订阅业务对观察者模式有一个简单的了解:

        观察者模式类图:

        

        Subject

        主题接口,也即可观察者(Observable),对象使用此接口注册为观察者,或者把自己从观察着中删除。每个主题可以有多个观察者。

        ConcreteSubject

        一个具体主题实现了主题接口,除了注册和撤销之外,具体主题还实现了notifyObservers()方法,这个方法用来在主题状态改变时更行所有的观察者。具体主题也可能有设置和获取状态的方法。

        Observer

        所有潜在的观察者必须实现观察者接口,这个接口只有update()方法,当主题改变时,它被调用。

        ConcreteObserver

        具体的观察者可以是任何实现了Observer接口的类。观察者必须注册具体主题,一边接收更新。

        基本原则:为了交互对象之间的松耦合设计而努力

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

        观察者模式中,一对多的关系体现在哪里?

        这个模式中,主题是具有状态的对象,并且可以控制这个状态,也就是说,是一个具有“状态”的主题。

        另一方面,观察者使用这些状态,虽然这些状态并不属于他们。有多个观察者,依靠主题来告诉他们主题状态何时变了。

        这就产生了一个关系,一个“主题”对多个“观察者”的关系

        松耦合设计威力体现在哪里?

        当两个对象松耦合,他们依然可以交互,但是不太清楚彼此的细节。观察者模式提供了一种对象设计,让主题和观察者松耦合。

        

        下面我们根据观察者模式来画出开篇WheatherStation的类图

        

        代码如下

        ISubject:

    import org.jpatterns.gof.ObserverPattern;
    
    @ObserverPattern(
    		comment="ISubject: Interface of Observer Pattern"
    )
    public interface ISubject {
    	void registerObserver(IObserver o);
    	void deleteObserver(IObserver o);
    	void notifyObservers();
    }
    

      IObserver:

    import org.jpatterns.gof.ObserverPattern;
    
    @ObserverPattern(
    		comment="IObserver: Interface of Observer Pattern"
    )
    public interface IObserver {
    	void update(float temp, float humidity, float pressure);
    }
    

      实现了ISubject接口的WheatherData:

    import java.util.ArrayList;
    
    import org.jpatterns.gof.ObserverPattern;
    
    @ObserverPattern(comment = "WheatherData: Concrete Subject of Observer Pattern")
    public class WheatherData implements ISubject {
    	private ArrayList<IObserver> observers;
    	private float temperature;
    	private float humidity;
    	private float pressure;
    
    	public WheatherData() {
    		this.observers = new ArrayList<IObserver>();
    	}
    
    	@Override
    	public void registerObserver(IObserver o) {
    		observers.add(o);
    	}
    
    	@Override
    	public void deleteObserver(IObserver o) {
    		if (observers.indexOf(o) >= 0) {
    			observers.remove(o);
    		}
    	}
    
    	@Override
    	public void notifyObservers() {
    		for (int i = 0; i < observers.size(); i++) {
    			IObserver observer = observers.get(i);
    			observer.update(temperature, humidity, pressure);
    		}
    	}
    
    	public void measurementsChanged() {
    		this.notifyObservers();
    	}
    
    	public void setMeasurements(float temperature, float humidity, float pressure) {
    		this.temperature = temperature;
    		this.humidity = humidity;
    		this.pressure = pressure;
    		this.measurementsChanged();
    	}
    }
    

      实现了IObserver接口的一个Bulletin:

    public class CurrentConditionsBulletin implements IObserver, IDisplayElement {
    
    	private float temperature;
    	private float humidity;
    	private ISubject wheatherData;
    
    	public CurrentConditionsBulletin(ISubject wheatherData) {
    		this.wheatherData = wheatherData;
    		wheatherData.registerObserver(this);
    	}
    
    	@Override
    	public void update(float temp, float humidity, float pressure) {
    		this.temperature = temp;
    		this.humidity = humidity;
    		this.display();
    	}
    
    	@Override
    	public void display() {
    		System.out.println("Current Conditions: " + this.temperature
    				+ "F Degrees and " + this.humidity + "% Humidity");
    	}
    }
    

      创建气象站,将程序run起来:

    public class WheatherStation {
    
    	public static void main(String[] args) {
    		WheatherData wheatherData = new WheatherData();
    		IObserver o1 = new CurrentConditionsBulletin(wheatherData);
    		IObserver o2 = new StatisticsBulletin(wheatherData);
    		IObserver o3 = new ForecastBulletin(wheatherData);
    		IObserver o4 = new CustomBulletin(wheatherData);//自定义的
    		wheatherData2.setMeasurements(20, 20, 20);
    	}
    
    }
    

      省略了好多注释啊,想好好理解,还是推荐去看HeadFirst吧,说实话,真本书真好!

          其实在java中已经有内置的观察者模式。java.util中的Observable(类)和Observer(接口)。但是缺点不少,例如违反了:针对接口变成,而不是针对实现编程和多用组合少用继承两个原则,而且起nofify也依赖顺序。

          观察者模式的要点:

          观察者定义了对象之间一对多的关系。

          主题用一个共同的接口来更新观察者。

          观察者和可观察者之间用松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口。

          使用此模式,可以从可观察者推或者拉数据。

          有多个观察者时,不可以依赖特定的通知次序。

          要注意java.util.Observable实现上带来的问题。

          如果有必要的话,可以实现自己的Observable。

        观察者模式的应用

        观察者模式的优点

        观察者和可观察者之间是抽象耦合

        建立了一套完整的触发链

        观察者模式的缺点

        多级触发的效率问题

        Java中消息的通知默认是顺序执行的,一个观察者卡壳,会影响整体的效率

        观察者模式的使用场景

        可拆分的关联行为场景

        事件多级触发的场景

        跨系统的消息交换场景

        

  • 相关阅读:
    第一章 数据库概论
    规范化
    qt mysql驱动问题解绝
    linux文件系统软链接硬链接
    linux命令总结
    shell应用技巧
    如何使滚动条保持在页面底部
    地图定位问题汇总
    webpack基本用法
    git异常操作解决办法合集
  • 原文地址:https://www.cnblogs.com/shitouer/p/2164048.html
Copyright © 2011-2022 走看看