zoukankan      html  css  js  c++  java
  • 观察者模式2(observer)

    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);
    } //所有的观察者必须实现update方法,以实现观察者接口
    public interface DisplayElement{ 
        public void display();//当需要显示时,调用此方法
        
    } 

    在WeatherData中实现主题接口:

    import java.util.ArrayList;
    
    public class WeatherData implements Subject
    {
        private ArrayList observers; //我们加上一个ArrayList来记录
        //观察者,此ArrayList是在构造器中建立的
        private float temperature;
        private float humidity;
        private float pressure;
        
        public  WeatherData(){
        observers=new ArrayList();
        }
        
        public void registerObserver(Observer o)
        {
           observers.add(o);//当注册观察者时,我们只要把它加到
           //ArrayList后面即可
         }
        
        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 void measurementsChanged(){
            notifyObservers();
            }
        
        public void setMeasurements(float temperature,float humidity,float pressure)
        {
            this.temperature=temperature;
            this.humidity=humidity;
            this.pressure=pressure;
            measurementsChanged();
        }
        //Weatherdata其他方法
    
    }       

    我们接着建立观察者(在我们例子中为布告板):

    第一个布告板:

     
    
    public class CurrentConditionsDisplay implements Observer,DisplayElement{
        //此对象实现了Observer接口,所以可以从WeatherData对象获得改变,它也实现了DisplayElement接口,显示
        private float temperature;
        private float humidity;
        private Subject weatherData;
        
        public CurrentConditionsDisplay(Subject weatherData)//构造器需要weatherData对象作为注册之用
        {
            this.weatherData=weatherData;
            weatherData.registerObserver(this);
        }
         
        public void update(float temperature,float humidity,float pressure)
        {
            this.temperature=temperature;
            this.humidity=humidity;
            //当update()被调用时,我们把温度和湿度保存起来,然后调用display
            display();
            
        }
        
        public void display()
        {
            System.out.println("Current conditions:"+temperature+"F degrees and "+humidity+"% humidity");
                   //display只是把温度和湿度显示出来
        }
    }

    第二个布告板为气压:

    public class ForecastDisplay implements Observer,DisplayElement{
        private float currentPressure = 29.92f;  
        private float lastPressure;
        private WeatherData weatherData;
        
        public ForecastDisplay(WeatherData weatherData)
        {
            this.weatherData=weatherData;
            weatherData.registerObserver(this);
            
        }
        public void update(float temp, float humidity, float pressure) 
        {
            lastPressure = currentPressure;
             currentPressure = pressure;
    
               display();
         }
        public void display() {
            System.out.print("Forecast: ");
            if (currentPressure > lastPressure) {
                System.out.println("Pressure"+currentPressure+"Improving weather on the way!");
            } else if (currentPressure == lastPressure) {
                System.out.println("Pressure"+currentPressure+"More of the same");
            } else if (currentPressure < lastPressure) {
                System.out.println("Pressure"+currentPressure+"Watch out for cooler, rainy weather");
            }
        }
    
    }

    当然,我们想增加多少个布告板都行,这里我们就不写了。

     现在可以启动气象站了,建立一个测试程序:

    public class WeatherStation {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            WeatherData weatherData=new WeatherData();
            CurrentConditionsDisplay currentDisplay=new CurrentConditionsDisplay(weatherData);
            ForecastDisplay forecastDisplay=new ForecastDisplay(weatherData);
        
            
            weatherData.setMeasurements(80, 65,30.4f);
            weatherData.setMeasurements(82,70,29.2f);
            
        }
    }

    运行结果如下:

    Current conditions:80.0F degrees and 65.0% humidity
    Forecast: Pressure30.4Improving weather on the way!
    Current conditions:82.0F degrees and 70.0% humidity
    Forecast: Pressure29.2Watch out for cooler, rainy weather

    以上我们的observer对象是用o来表示的,与零0很像,很容易写错,尽量不要用o来写。

    使用java内置的观察着模式

    java.util包的observer接口与Observable类


    public interface Observer
    A class can implement the Observer interface when it wants to be informed of changes in observable objects.
     
    void update(Observable o,
              Object arg)
    This method is called whenever the observed object is changed. An application calls an Observable object's notifyObservers method to have all the object's observers notified of the change.
    Parameters:
    o - the observable object.
    arg - an argument passed to the notifyObservers method.
     

    WeatherData,我们以前所称的主题现在是改称为 “可观察者”(Observerable)。 我们不需要提供register(),remove)(和notifyObservers()方法,因为我们已经从超类中继承了这些行为。

    java内置的观察者模式如何运作

    把对象变成观察者。。。。。。

       如同以前一样,实现观察者接口(java.util.Observer),然后调用任何Observerable对象的addObserver()方法,不想再当观察者,调用deleteObserver()方法就可以了。

    可观察者要如何送出通知。。。

      首先,你需要利用扩展java.util.Observerable接口产生“可观察者”类,然后,需要2个步骤;

        1.先调用setChanged()方法,标记状态已经改变的事实。

        2.然后调用2种notifyObservers()方法中的任何一个:

    notifyObservers() 或notifyObservers(object arg);(当通知时,此版本可以传送任何的数据对象给每一个观察者)

    观察者如何接受通知

     同以前一样,观察者实现了更新的方法,但是方法的签名不一样:

     update(Observable o,Object arg);

    主题本身当做第一个变量,好让观察者知道是哪个主题通知他的 。 Object arg,这正是传入notifyObservers()的数据对象。如果没有说明则为空。

    如果你想“推”push数据给观察者,你可以把数据当做数据对象传送给notifyObservers(arg)方法,否则,观察者就必须从可观察者队长中“拉”pull数据。如何拉数据?我们再做一遍气象站你很快就能看到。

    setChanged()方法用来标记状态已经改变的事实,好让notifyObservers()知道当他被调用时应该更新观察者。如果调用notifyObservers()之前没有先调用setChanged(),观察者就“不会”被通知。让我们看看Observerable内部:

    这样做有其必要性。setChanged()方法可以让你在更新观察者时,有更多的弹性,你可以更适当地通知观察者。比方说,如果没有setChanged()方法,我们的气象站测量时如此敏锐,以至于温度读数每十分之一度就会更新,这会造成WeatherData对象持续不断地通知观察者,我们并不希望这样的事情发生。如果我们希望半度以上才更新,就可以在温度计差距到达半度时,调用setChanged(),进行有效的更新。

     你也许不会经常用到此功能,但是把这样的功能准备好,当需要时马上就可以使用。总之,你需要调用setChanged(),以便通知开始运转。如果此功能在某些地方对你有帮助,你可能也需要clearChanged()方法,将changed状态设置为false。另外也有一个hasChanged()方法,告诉你当前的状态。

     利用内置的支持重做栖气象站

      首先,把WeatherData改成使用Observable

    package Observable;
    
    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()之前,要调用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;
        }
        
    }

    现在,让我们重做显示面板:

    public class CurrentConditionDisplay  implements Observer{
       
        Observable observable;
        private float temperature;
        private float humidity;
        
        public CurrentConditionDisplay(Observable observable) {
            this.observable=observable;
            observable.addObserver(this);
            
        }
    
        @Override
        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");
        }
        
    }

    测试类还是一样:

    public class TestMain {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
           WeatherData weatherData=new WeatherData();
           
           CurrentConditionDisplay currentConditionDisplay=new CurrentConditionDisplay(weatherData);
           
           weatherData.setMeasurements(10,10,10);
           weatherData.setMeasurements(82, 70, 29.2f);
            weatherData.setMeasurements(78, 90, 29.2f);
            
        }
    
    }

    输出:

    Current conditions: 10.0F degrees and 10.0% humidity
    Current conditions: 82.0F degrees and 70.0% humidity
    Current conditions: 78.0F degrees and 90.0% humidity

    为了test setChanged()是否起到了作用,我们改变下代码:

    if(this.temperature>10)
    {

      setChanged();

    }

    notifyObservers();

    第1个数据就不会输出了。

    java.util.Observable的黑暗面

       是的!你注意到了,可观察者是一个“类”而不是一个接口,更糟糕的是,他甚至没有实现一个接口。不幸的是,Observable的实现有许多问题,限制了他的使用和复用。

      Observable是一个“类”会带来什么问题呢?

     首先,因为是一个类,你必须设计一个类来继承他。如果某类想同时具有Observable类和另一个超类的行为,就会陷入两难,毕竟java不支持多重继承。这限制了Observable的复用潜力(而增加复用潜力不正是我们使用模式最原始的动机吗?)

      再者,因为没有Observable接口,所以你无法建立自己的实现。

    在jdk中,事件监听swing中用到了观察者模式,在一个button上注册一个监听器,如果点击就会想要的监听器事件发生。

  • 相关阅读:
    python之RabbitMQ
    RHEL 使用epel源
    Python操作 Memcache
    LOJ #6053. 简单的函数 (min25筛裸题)
    [51Nod
    Min25筛学习 + 【51nod1847】奇怪的数学题(Min_25筛+杜教筛)
    BZOJ 3331: [BeiJing2013]压力 (点双 圆方树 树链剖分 线段树)
    BZOJ 2125: 最短路(仙人掌 圆方树)
    模拟赛题解 naive (二分)
    BZOJ 2286 [Sdoi2011]消耗战 (虚树模板题)
  • 原文地址:https://www.cnblogs.com/youxin/p/2591720.html
Copyright © 2011-2022 走看看