Java設計模式----觀察者模式---天氣預報案例詳解

一、天氣預報項目需求分析

       天氣預報的需求,具體要求如下: 

       1. 氣象站可以將每天測到的溫度、溼度、氣壓、PM2.5等以公告的形式發佈到自己的網站或者第三方。

       2. 有對外的接口可以被其他系統所調用,比如新浪等能夠接入該接口來獲取數據。

      3. 提供關鍵數據的接口,溫度、溼度、第三方等信息。

      4.測量數據更新時,要能實時地通知給第三方。

 

二、方案實現

       1.普通方案 

  

  currentConditions類主要負責顯示當前的天氣信息,接收氣象站發過來新的溫度、溼度等信息並更新當前信息。 

    

package com.exam.observer;
/**
 *author:bingbing
 *日期:2020年5月4日
 *時間:下午6:50:20
 *當前情況類
 */

public class CurrentConditions {
	

	private String temperature;
	
	private String humidity;
	
	private String pressure;
	
	
	
	
	
	/**
	 * 
	 * 顯示天氣信息
	 */
	private void display() {
	     System.out.println("當前溫度爲:"+this.temperature);
	     System.out.println("當前溼度爲:"+this.humidity);
	     System.out.println("當前大氣壓爲:"+this.pressure);
	}
	
	
	
	
	
	/**
	 *更新天氣信息,更新完後將天氣信息顯示出來
	 */
	public void update(String temperature,String  humidity,String pressure) {
		this.temperature=temperature;
		this.humidity=humidity;
		this.pressure=pressure;
		display();

	}
	
	
	
	

}

 

WeatherData類,主要提供獲取信息的接口,然後將信息的更新傳給currentConditions:

package com.exam.observer;
/**
 *author:bingbing
 *日期:2020年5月4日
 *時間:下午6:47:35
 *天氣信息類
 */

public class WeatherData {
	
	
	private String temperature;
	
	private String humidity;
	
	private String pressure;
	
	private CurrentConditions current;
	
	public WeatherData(CurrentConditions current){
		this.current=current;
	}
	

	public String getTemperature() {
		return temperature;
	}

	

	public String getHumidity() {
		return humidity;
	}

	

	public String getPressure() {
		return pressure;
	}
	
	public void setData(String temperature,String humidity,String pressure) {
		this.temperature=temperature;
		this.humidity=humidity;
		this.pressure=pressure;	
//設置信息時也要調用dataChange()方法。
		dataChange();
	}

	
	

	public void dataChange() {
		current.update(getTemperature(), getHumidity(), getPressure());
	}
	
	
	
	
	

}

測試類:

package com.exam.observer;
/**
 *author:bingbing
 *日期:2020年5月4日
 *時間:下午6:53:29
 *測試類
 */

public class Test {
	
	
	public static void main(String[] args) {
		
		CurrentConditions current=new CurrentConditions();
		WeatherData data=new WeatherData(current);
		data.setData("1", "2", "3");
		data.setData("4", "5", "6");
		
	}

}

打印結果爲:

當前溫度爲:1
當前溼度爲:2
當前大氣壓爲:3
當前溫度爲:4
當前溼度爲:5
當前大氣壓爲:6
  

  此方案實現了基本的功能,氣象站(WeatherData)可以發佈信息(setData()),然後能夠實時的更新信息dataChange()。

   但此方案存在一定的問題,分析:
   1)其他三方無法進入氣象站獲取到數據的問題。

   2) 無法在運行時動態的添加第三方(新浪)。

   3) 違反OCP原則(軟件應該對擴展開放,對修改關閉)--> 觀察者模式。

 

    2.改進方案---觀察者模式

       在普通方案的基礎上進行改進,新增subject接口和observer接口,subject接口用來管理多個奶站或者氣象站的信息,將信息發佈出去。 observer接口則可以用來給多個第三方接入來獲取奶站或者氣象站的數據。

       UML圖如下:

       奶站或者氣象站接口(Subject):

        

package com.exam.observer.improve;
/**
 *author:bingbing
 *日期:2020年5月4日
 *時間:下午7:58:18
 */

public interface Subject {
	
	
	/**
	 * 註冊observer
	 */
	public void registerObserver(Observer o);
	
	/**
	 * 移除observer
	 */
	public void removeObserver(Observer o);
	
	/**
	 * 通知所有的observer
	 */
	public void notifyAllObserver();

}

 

    天氣氣象站(WeatherData):  管理氣象信息和所有的觀察者。

package com.exam.observer.improve;

import java.util.ArrayList;
import java.util.List;

/**
 *author:bingbing
 *日期:2020年5月4日
 *時間:下午7:51:26
 *核心信息,管理觀察者
 *
 */

public class WeatherData implements Subject{
	
	
    private String temperature;
	
	private String humidity;
	
	private String pressure;
	
	
	private List<Observer> observers;
	
	public WeatherData() {
		observers=new ArrayList<Observer>();
	}


	public void registerObserver(Observer o) {
		observers.add(o);	
	}


	public void removeObserver(Observer o) {
		observers.remove(o);
		
	}
	
	
	
	
	public String getTemperature() {
		return temperature;
	}




	public String getHumidity() {
		return humidity;
	}




	public String getPressure() {
		return pressure;
	}




	/**
	 * 發佈信息
	 */
	public void setData(String temperature,String humidity,String pressure) {
		this.temperature=temperature;
		this.humidity=humidity;
		this.pressure=pressure;
		update(); 
	}
	
	
	/**
	 * 通知觀察者修改信息
	 */
	public void update() {
		notifyAllObserver();
	}
	


	/**
	 * 通知所有的觀察者
	 */
	public void notifyAllObserver() {
		for(int i=0;i<observers.size();i++) {
			observers.get(i).update(getTemperature(), getHumidity(), getPressure());
		}
	}
	
	
	

}

     觀察者接口(Observer):

package com.exam.observer.improve;
/**
 *author:bingbing
 *日期:2020年5月4日
 *時間:下午7:51:53
 *觀察者接口,負責接收奶站或者氣象站的數據並更新當前擁有的數據。
 */

public interface Observer {
	
	void update(String temperature,String humidity,String pressure);

}

  不同的觀察者:

 

package com.exam.observer.improve;
/**
 *author:bingbing
 *日期:2020年5月4日
 *時間:下午7:54:33
 */

public class CurrentConditions implements Observer{
	
   private String temperature;
	
	private String humidity;
	
	private String pressure;
	

	public void display() {
	     System.out.println("當前溫度爲:"+this.temperature);
	     System.out.println("當前溼度爲:"+this.humidity);
	     System.out.println("當前大氣壓爲:"+this.pressure);
	}



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

}

 百度:

package com.exam.observer.improve;
/**
 *author:bingbing
 *日期:2020年5月4日
 *時間:下午8:13:24
 */

public class Baidu  implements Observer{
	
	   private String temperature;
		
		private String humidity;
		
		private String pressure;
		

		public void display() {
		     System.out.println("百度當前溫度爲:"+this.temperature);
		     System.out.println("百度當前溼度爲:"+this.humidity);
		     System.out.println("百度當前大氣壓爲:"+this.pressure);
		}



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

}

測試類:

package com.exam.observer.improve;
/**
 *author:bingbing
 *日期:2020年5月4日
 *時間:下午8:11:12
 */

public class Test {
	
	
	public static void main(String[] args) {
		
		WeatherData data=new WeatherData();
		data.registerObserver(new CurrentConditions());
		data.setData("1", "2", "3");
		data.registerObserver(new Baidu());
		data.setData("3", "4", "5");	
	}

}

打印結果:

當前溫度爲:1
當前溼度爲:2
當前大氣壓爲:3
當前溫度爲:3
當前溼度爲:4
當前大氣壓爲:5
百度當前溫度爲:3
百度當前溼度爲:4
百度當前大氣壓爲:5

 

三、觀測者模式在JDK源碼中的應用

   JdkObservable類就使用了觀察者模式

     Observable類的源碼如下:

/*
 * Copyright (c) 1994, 2012, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.util;

/**
 * This class represents an observable object, or "data"
 * in the model-view paradigm. It can be subclassed to represent an
 * object that the application wants to have observed.
 * <p>
 * An observable object can have one or more observers. An observer
 * may be any object that implements interface <tt>Observer</tt>. After an
 * observable instance changes, an application calling the
 * <code>Observable</code>'s <code>notifyObservers</code> method
 * causes all of its observers to be notified of the change by a call
 * to their <code>update</code> method.
 * <p>
 * The order in which notifications will be delivered is unspecified.
 * The default implementation provided in the Observable class will
 * notify Observers in the order in which they registered interest, but
 * subclasses may change this order, use no guaranteed order, deliver
 * notifications on separate threads, or may guarantee that their
 * subclass follows this order, as they choose.
 * <p>
 * Note that this notification mechanism has nothing to do with threads
 * and is completely separate from the <tt>wait</tt> and <tt>notify</tt>
 * mechanism of class <tt>Object</tt>.
 * <p>
 * When an observable object is newly created, its set of observers is
 * empty. Two observers are considered the same if and only if the
 * <tt>equals</tt> method returns true for them.
 *
 * @author  Chris Warth
 * @see     java.util.Observable#notifyObservers()
 * @see     java.util.Observable#notifyObservers(java.lang.Object)
 * @see     java.util.Observer
 * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
 * @since   JDK1.0
 */
public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs;

    /** Construct an Observable with zero Observers. */

    public Observable() {
        obs = new Vector<>();
    }

    /**
     * Adds an observer to the set of observers for this object, provided
     * that it is not the same as some observer already in the set.
     * The order in which notifications will be delivered to multiple
     * observers is not specified. See the class comment.
     *
     * @param   o   an observer to be added.
     * @throws NullPointerException   if the parameter o is null.
     */
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

    /**
     * Deletes an observer from the set of observers of this object.
     * Passing <CODE>null</CODE> to this method will have no effect.
     * @param   o   the observer to be deleted.
     */
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

    /**
     * If this object has changed, as indicated by the
     * <code>hasChanged</code> method, then notify all of its observers
     * and then call the <code>clearChanged</code> method to
     * indicate that this object has no longer changed.
     * <p>
     * Each observer has its <code>update</code> method called with two
     * arguments: this observable object and <code>null</code>. In other
     * words, this method is equivalent to:
     * <blockquote><tt>
     * notifyObservers(null)</tt></blockquote>
     *
     * @see     java.util.Observable#clearChanged()
     * @see     java.util.Observable#hasChanged()
     * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
     */
    public void notifyObservers() {
        notifyObservers(null);
    }

    /**
     * If this object has changed, as indicated by the
     * <code>hasChanged</code> method, then notify all of its observers
     * and then call the <code>clearChanged</code> method to indicate
     * that this object has no longer changed.
     * <p>
     * Each observer has its <code>update</code> method called with two
     * arguments: this observable object and the <code>arg</code> argument.
     *
     * @param   arg   any object.
     * @see     java.util.Observable#clearChanged()
     * @see     java.util.Observable#hasChanged()
     * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
     */
    public void notifyObservers(Object arg) {
        /*
         * a temporary array buffer, used as a snapshot of the state of
         * current Observers.
         */
        Object[] arrLocal;

        synchronized (this) {
            /* We don't want the Observer doing callbacks into
             * arbitrary code while holding its own Monitor.
             * The code where we extract each Observable from
             * the Vector and store the state of the Observer
             * needs synchronization, but notifying observers
             * does not (should not).  The worst result of any
             * potential race-condition here is that:
             * 1) a newly-added Observer will miss a
             *   notification in progress
             * 2) a recently unregistered Observer will be
             *   wrongly notified when it doesn't care
             */
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

    /**
     * Clears the observer list so that this object no longer has any observers.
     */
    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }

    /**
     * Marks this <tt>Observable</tt> object as having been changed; the
     * <tt>hasChanged</tt> method will now return <tt>true</tt>.
     */
    protected synchronized void setChanged() {
        changed = true;
    }

    /**
     * Indicates that this object has no longer changed, or that it has
     * already notified all of its observers of its most recent change,
     * so that the <tt>hasChanged</tt> method will now return <tt>false</tt>.
     * This method is called automatically by the
     * <code>notifyObservers</code> methods.
     *
     * @see     java.util.Observable#notifyObservers()
     * @see     java.util.Observable#notifyObservers(java.lang.Object)
     */
    protected synchronized void clearChanged() {
        changed = false;
    }

    /**
     * Tests if this object has changed.
     *
     * @return  <code>true</code> if and only if the <code>setChanged</code>
     *          method has been called more recently than the
     *          <code>clearChanged</code> method on this object;
     *          <code>false</code> otherwise.
     * @see     java.util.Observable#clearChanged()
     * @see     java.util.Observable#setChanged()
     */
    public synchronized boolean hasChanged() {
        return changed;
    }

    /**
     * Returns the number of observers of this <tt>Observable</tt> object.
     *
     * @return  the number of observers of this object.
     */
    public synchronized int countObservers() {
        return obs.size();
    }
}

   Observer接口:

/*
 * Copyright (c) 1994, 1998, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package java.util;

/**
 * A class can implement the <code>Observer</code> interface when it
 * wants to be informed of changes in observable objects.
 *
 * @author  Chris Warth
 * @see     java.util.Observable
 * @since   JDK1.0
 */
public interface Observer {
    /**
     * This method is called whenever the observed object is changed. An
     * application calls an <tt>Observable</tt> object's
     * <code>notifyObservers</code> method to have all the object's
     * observers notified of the change.
     *
     * @param   o     the observable object.
     * @param   arg   an argument passed to the <code>notifyObservers</code>
     *                 method.
     */
    void update(Observable o, Object arg);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章