設計模式---觀察者模式

---------------------------------根據《Head First 設計模式》整理記憶----------------------------------

觀察者模式:定義了對象之間的一對多依賴,這樣一來,當一個對象改變狀態時,它的所有依賴者都會收到通知並自動更新(是不是感覺很神奇!!!)。

      小A公司接了一個爲氣象站開發Internet氣象觀測站的項目。項目開發要求:由氣象站提供一個WeatherData對象負責追蹤目前的天氣狀況(溫度、溼度、氣壓),要求公司開發一個應用,有三種佈告板,分別及時更新顯示目前的狀況,而且這個應用能夠易於擴展開發新的佈告板。

現在我們開始開發吧

1.第一次嘗試

複製代碼
 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);

        }
        //這裏是其他WeatherData方法
    }
複製代碼

解析下:代碼看上去蠻漂亮的,但是想想我們的設計原則
1.找出應用中可能需要變化之處,把它們獨立出來,不要和那些不需要變化的代碼混在一起。
2.針對接口編程,而不是針對實現編程。

2.第二次嘗試:觀察者模式

     觀察者模式主要的兩個對象是主題和觀察者,觀察者依賴於此主題,只要主題狀態一有變化,觀察者就會被通知。根據通知的風格觀察者可能因此新值而更新。

接下來我們來看下觀察者模式的一種常見做法:Subject和Observer接口的類設計的做法。

 public  interface Subject
    {
       void registerObserver(Observer o);//以觀察者作爲變量,該觀察者是用來註冊的。
       void removeObserver(Observer o);//以觀察者作爲變量,該觀察者是用來刪除的。
        void notifyObservers();//當主題狀態改變時,這個方法會被調用,以通知所有觀察者。
    }
 public interface Observer
    {
        void update(float temp,float humidity,float pressure);//所有觀察者都必須實現updata()方法,以實現觀察者接口。
    }
複製代碼
    class WeatherData:Subject 
    {
        //ArrayList 來記錄依賴主題的所有觀察者
        private ArrayList observers;
        private float temperature;
        private float humidity;
        private float pressure;

        public WeatherData() 
        {
            observers = new ArrayList();
        }
        #region Subject 成員
        //當註冊觀察者時,我們只要把它加到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 );
            }
        }
        //有趣的地方來了!在這裏,我們把狀態告訴每一個觀察者。
        //因爲觀察者都實現了update(),所以我們知道如何通知它們。
        public void notifyObservers()
        {
            for (int i = observers.Count-1;i >=0 ;i -- )
            {
                Observer observer = (Observer)observers[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();
        }

        #endregion
    }
複製代碼
  public interface DisplayElement
    {
        void display();//當佈告板顯示時,調用這個方法。
    }
複製代碼
 
//這裏我就寫了一個佈告板,有興趣的朋友可以自己多寫幾個佈告板,體驗下觀察者模式的精髓。
class CurrentConditionsDisplay:Observer ,DisplayElement 
    {
        private float temperature;
        private float humidity;
        private Subject weatherData;

        public CurrentConditionsDisplay(Subject weatherData)//構造器需要 weatherData對象,作爲註冊之用。
        {
            this.weatherData = weatherData;
            weatherData.registerObserver(this );
        }
        #region DisplayElement 成員
        //把最近的溫度和溼度顯示出來
        public void display()
        {
            Console.WriteLine("Current conditions:"+temperature +
                "F degrees and"+humidity +"% humidity");
        }

        #endregion

        #region Observer 成員
        //當update()被調用時,我們把溫度和溼度保存起來,然後調用display().
        public void update(float temp, float humidity, float pressure)
        {
            this.temperature = temp;
            this.humidity = humidity;
            display();
        }

        #endregion
    }
複製代碼

測試下:

複製代碼
static void Main(string[] args)
        {
            WeatherData weatherData = new WeatherData();//首先,建立一個WeatherData對象
            //建立一個佈告板並把WeatherData對象傳給它,currentDisplay 在構造器中往WeatherData對象註冊自己
            CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData );
            //模擬新的氣象測量
            weatherData.setMeasurements(80,65,30.4f);
            weatherData.setMeasurements(82,70,29.2f);
            weatherData.setMeasurements(78,90,29.2f);
            Console.ReadKey();
        }
複製代碼

調試顯示:


----------------------------------分割線,又重新寫了一下代碼------------------------------------------------------------

首先三個接口:

1.subject接口,主題接口,當自己更新的時候用來發送給觀察者資料,因此觀察者需要過來註冊一下,否則主題大人不知道,也可以不訂這個主題,當變動的時候,需要通知他們,因此至少三個method。

package ObserverPattern;

/**
 * @author 作者 E-mail:
 * @version 創建時間:2015年7月9日 下午7:44:23 類說明
 */
public interface Subject {
	public void registerObserver(Observer o);

	public void removeObserver(Observer o);

	public void notifyObserver();
}

2.Obsever接口,當subject變化的時候,Observer需要提供update方法,使得主題可以給他們發送及時信息。

package ObserverPattern;

/**
 * @author 作者 E-mail:
 * @version 創建時間:2015年7月9日 下午7:45:56 類說明
 */
public interface Observer {
	public void update(float temp, float humidity, float pressure);
}

3.DisplayElement接口,用來顯示狀態的接口

package ObserverPattern;

/**
 * @author 作者 E-mail:
 * @version 創建時間:2015年7月9日 下午7:47:56 類說明
 */
public interface DisplayElement {
	public void display();
}

4.WeatherData類,實現Subject接口

package ObserverPattern;

import java.util.ArrayList;

/**
 * @author 作者 E-mail:
 * @version 創建時間:2015年7月9日 下午7:48:41 類說明
 */
public class WeatherData implements Subject {
	private ArrayList<Observer> observers;
	private float temperature;
	private float humidity;
	private float pressure;

	public WeatherData() {//註冊到這個地方
		observers = new ArrayList<Observer>();
	}

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

	public void removeObserver(Observer o) {
		int i = observers.indexOf(o);
		if (i >= 0) {
			observers.remove(o);
		}
	}

	public void notifyObserver() {
		for (int i = 0; i < observers.size(); i++) {
			Observer observer = observers.get(i);
			observer.update(temperature, humidity, pressure);
		}
	}

	public void measurementsChanged() {
		notifyObserver();
	}

	public void setMeasurements(float temperature, float humidity, float pressure) {
		this.temperature = temperature;
		this.humidity = humidity;
		this.pressure = pressure;
		measurementsChanged();
	}
}

5.測試類

package ObserverPattern;

/**
 * @author 作者 E-mail:
 * @version 創建時間:2015年7月9日 下午8:04:51 類說明
 */
public class testMain {

	public static void main(String[] args) {
		WeatherData aTest = new WeatherData();
		CuurentConditionDisplay aCuurent = new CuurentConditionDisplay(aTest);
		StaticticsDisplay aStatictics = new StaticticsDisplay(aTest);
		ForecastDisplay aForecast = new ForecastDisplay(aTest);
		aTest.setMeasurements(11, 12, 13);
	}
}

6.三個實現Oberserver,DisplayElement接口的顯示狀態的類:

package ObserverPattern;

/**
 * @author 作者 E-mail:
 * @version 創建時間:2015年7月9日 下午8:08:13 類說明
 */
public class CuurentConditionDisplay implements Observer, DisplayElement {
	private float temperature;
	private float humidity;
	private float pressure;
	private Subject WeatherData;
	
	public CuurentConditionDisplay(Subject WeatherData){
		this.WeatherData = WeatherData;
		WeatherData.registerObserver(this);
	}
	public void update(float temperature, float humidity, float pressure) {
		this.temperature = temperature;
		this.humidity = humidity;
		this.pressure = pressure;
		display();
	};

	public void display() {
		System.out.println("--------CuurentConditionDisplay-------------");
		System.out.println("temperature:" + temperature + "humidity:"
				+ humidity + "pressure:" + pressure);
	};
}


package ObserverPattern;

/**
 * @author 作者 E-mail:
 * @version 創建時間:2015年7月9日 下午8:08:13 類說明
 */
public class StaticticsDisplay implements Observer, DisplayElement {
	private float temperature;
	private float humidity;
	private float pressure;
	private Subject WeatherData;
	public StaticticsDisplay(Subject WeatherData){
		this.WeatherData = WeatherData;
		WeatherData.registerObserver(this);
	}
	public void update(float temperature, float humidity, float pressure) {
		this.temperature = temperature;
		this.humidity = humidity;
		this.pressure = pressure;
		display();
	};

	public void display() {
		System.out.println("--------StaticticsDisplay-------------");
		System.out.println("temperature:" + temperature + "humidity:"
				+ humidity + "pressure:" + pressure);
	};
}

package ObserverPattern;

/**
 * @author 作者 E-mail:
 * @version 創建時間:2015年7月9日 下午8:08:13 類說明
 */
public class ForecastDisplay implements Observer, DisplayElement {
	private float temperature;
	private float humidity;
	private float pressure;
	private Subject WeatherData;

	public ForecastDisplay(Subject WeatherData) {
		this.WeatherData = WeatherData;
		WeatherData.registerObserver(this);
	}

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

	public void display() {
		System.out.println("--------ForecastDisplay-------------");
		System.out.println("temperature:" + temperature + "humidity:"
				+ humidity + "pressure:" + pressure);
	};
}


觀察者模式總結

優點

1. 觀察者模式解除了主題和具體觀察者的耦合,讓耦合的雙方都依賴於抽象,而不是依賴具體。從而使得各自的變化都不會影響另一邊的變化。

缺點

1.依賴關係並未完全解除,抽象通知者依舊依賴抽象的觀察者。

適用場景

1 當一個對象的改變需要給變其它對象時,而且它不知道具體有多少個對象有待改變時。

2 一個抽象某型有兩個方面,當其中一個方面依賴於另一個方面,這時用觀察者模式可以將這兩者封裝在獨立的對象中使它們各自獨立地改變和複用。



---------附全部源代碼:http://download.csdn.net/detail/bingbing8219/8885141------------------------------
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章