本章目錄如下:
????????一、階段一
????????二悔雹、階段二
????????三益涧、設(shè)計(jì)原則總結(jié)
本章需求是設(shè)計(jì)一個(gè)氣象展示應(yīng)用扭弧,并通過代碼迭代伊磺、優(yōu)化過程得出前人的設(shè)計(jì)經(jīng)驗(yàn)之一觀察者模式续崖。下面以代碼的迭代演化過程為線索介紹像吻。
一惭每、階段一
需求:氣象監(jiān)測站希望創(chuàng)建一個(gè)氣象展示應(yīng)用置鼻,默認(rèn)有三個(gè)布告板蛛碌,且允許第三方接入/離開誊辉。監(jiān)測站提供WeatherData類,在氣象數(shù)據(jù)變化時(shí)氣象站調(diào)用WeatherData對象的measurementsChanged方法來更新氣象數(shù)據(jù)蛙紫。
需求分析:我們只需要在WeatherData類的measurementsChanged方法中獲取氣象數(shù)據(jù)途戒,并更新我們的氣象板即可矢渊。
代碼實(shí)現(xiàn)如下:
public class WeatherData{
????????//實(shí)例變量聲明秒赤,氣象站已經(jīng)實(shí)現(xiàn)
????public void measurementsChanged() {
?????????//首先鞍泉,調(diào)用 WeatherData 的三個(gè)getXxx()方法,以取得最近的測量值仗处。這些getXxx()方法氣象站已經(jīng)實(shí)現(xiàn)好了眯勾。
????????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方法婆誓,,氣象站已經(jīng)實(shí)現(xiàn)
}
該實(shí)現(xiàn)的缺點(diǎn):面對需求也颤,首先要找出變化洋幻,將變化分離出來,然后在依據(jù)設(shè)計(jì)原則挑選合適的設(shè)計(jì)模式翅娶。本章需求的變化之處是允許第三方接入文留,那么我們就需要將“允許第三方接入”功能分離出來好唯,如果像階段一的代碼一樣,不分離的話會(huì)使代碼不能擴(kuò)展和維護(hù)燥翅。下面我們進(jìn)入第二階段:優(yōu)化骑篙。
二、階段二
從報(bào)紙訂閱的角度來講森书,觀察者模式=出版者+訂閱者靶端;從理論定義角度來講,觀察者模式=主題(Subject)/可觀察者(Observable)+觀察者(Observer)凛膏。定義:觀察者模式定義了對象之間的一對多依賴杨名, 這樣一來,當(dāng)一個(gè)對象改變狀態(tài)時(shí)猖毫,它的所有依賴者都會(huì)收到通知并自動(dòng)更新台谍。觀察者模式類圖如下:
為什么要在主題實(shí)現(xiàn)類中加入一些get方法?因?yàn)橹黝}發(fā)送的信息可能有很多是觀察者不需要的數(shù)據(jù)吁断,主題實(shí)現(xiàn)類中加入get方法可以使觀察者接收到主題對象推送時(shí)主動(dòng)去拉去數(shù)據(jù)趁蕊。
類圖解釋說明:
? ? (1)、主期只知道觀察者實(shí)現(xiàn)了某個(gè)核口(也就是Observer接口) 仔役,主題不需要知道觀察者的具體類是誰介衔,做了些什么或其他任何細(xì)節(jié)。
? ? (2)骂因、任何時(shí)候我們都可以增加新的觀察者炎咖,因?yàn)橹黝}唯依賴的東西是一個(gè)實(shí)現(xiàn)Observer接口的對象列表,所以我們可以隨時(shí)增加觀察者寒波。
? ? (3)乘盼、有新類型的觀察者出現(xiàn)時(shí),主題的代碼不需要修改俄烁。
? ? (4)绸栅、因?yàn)閮烧呤撬珊系模淖冎黝}或觀察者其中一方页屠,并不會(huì)影響另一方粹胯,所以只要他們之間的接口仍被遵守,我們就可以自由地改變他們辰企。所以可以獨(dú)立地復(fù)用主題或觀察者风纠。
松耦合的威力:當(dāng)兩個(gè)對象之間松耦合,它們依然可以交互牢贸,但是不太清楚彼此的細(xì)節(jié)竹观。松糊合的設(shè)計(jì)之所以能讓我們建立有彈性的00系統(tǒng),能夠應(yīng)對變化,是因?yàn)閷ο笾g的互相依賴降到了最低臭增。觀察者模式讓主題和觀察者之間松耦合懂酱。
設(shè)計(jì)原則3:為了交互對象之間的松耦合設(shè)計(jì)而努力。
根據(jù)以上對觀察者模式的說明和新的設(shè)計(jì)原則的了解誊抛,我們將氣象站的新類圖設(shè)計(jì)如下:
氣象站代碼如下:
public interface Subject {//Subject接口
????public void registerObserver(Observer o);
????public void removeObserver(Observer o);
????public void notifyObservers();
}
public interface Observer {//Observer接口
????public void update(float temp, float humidity, float pressure);
}
public interface DisplayElement {//DisplayElement接口
????public void display();
}
=============================氣象站subject實(shí)現(xiàn)類======================
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(i);
????????}
????}
????public void notifyObservers() {
????????for (Observer observer : observers) {
????????????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();
????}
????public float getTemperature() {
????????return temperature;
????}
????public float getHumidity() {
????????return humidity;
????}
????public float getPressure() {
????????return pressure;
????}
}
==========================當(dāng)前狀況布告板============================
public class CurrentConditionsDisplay implements Observer, DisplayElement {
????private float temperature;
????private float humidity;
????private Subject weatherData;
????public CurrentConditionsDisplay(Subject weatherData) {
????????this.weatherData = weatherData;
????????weatherData.registerObserver(this);
????}
????public void update(float temperature, float humidity, float pressure) {
????????this.temperature = temperature;
????????this.humidity = humidity;
????????display();
????}
????public void display() {
????????System.out.println("Current conditions: " + temperature? + "F degrees and " + humidity + "% humidity");
????}
}//統(tǒng)計(jì)布告板列牺、預(yù)測布告板請參考源碼
=============================測試氣象站==============================
public class WeatherStation {
????public static void main(String[] args) {
????????WeatherData weatherData = new WeatherData();
????????CurrentConditionsDisplay currentDisplay =? new CurrentConditionsDisplay(weatherData);
????????StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
????????ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
????????weatherData.setMeasurements(80, 65, 30.4f);
????????weatherData.setMeasurements(82, 70, 29.2f);
????????weatherData.setMeasurements(78, 90, 29.2f);
????}
}
Java API內(nèi)置了觀察者模式,即java.util包 (package) 中的Observable類與Observer接口拗窃。Observable類中實(shí)現(xiàn)了addObserver(Observer o)瞎领、deleteObserver(Observer o)、notifyObservers()并炮、setChanged()方法默刚,主題實(shí)現(xiàn)類繼承該類即可使用這些方法。需要注意兩點(diǎn):
? ? ? ? (1)逃魄、使用前需要導(dǎo)入import java.util.Observable和import java.util.Observer包
? ? ? ? (2)荤西、調(diào)用notifyObservers()之前需要先調(diào)用setChanged()來指示狀態(tài)已經(jīng)改變。
三伍俘、設(shè)計(jì)原則總結(jié)
設(shè)計(jì)原則1:找出應(yīng)用中可能需要變化之處邪锌,把它們獨(dú)立出來,不要和那些不需要變化 T個(gè)T的代碼混在一起癌瘾。該設(shè)計(jì)原則作用: “把會(huì)變化的部分取出并封裝起來觅丰,以便以后可以輕易地改動(dòng)或擴(kuò)充此部分,而不影響不需要變化的其他部分”妨退。
設(shè)計(jì)原則2:針對接口編程妇萄,而不是針對實(shí)現(xiàn)編程。===我的理解是這個(gè)原則的使用優(yōu)先級排在“設(shè)計(jì)原則1:變化原則”之后咬荷,即該原則是一個(gè)在大模式已確定需要實(shí)現(xiàn)具體類時(shí)針對實(shí)現(xiàn)類采用的原則冠句,針對范圍比較小。
設(shè)計(jì)原則3:為了交互對象之間的松耦合設(shè)計(jì)而努力幸乒。