java設計模式-觀察者模式,裝飾者模式

1.1定義

慨念:定義了對象之間的一對多的依賴,這樣一來,當一個對象改變狀態時,它的全部依賴者都會收到通知並自動更新java

即:主題和觀察者定義了一對多的關係,觀察者依賴於主題,只要主題發生變化,觀察者就會被通知。編程

目的:一個對象狀態改變給其餘對象通知的問題,並且要考慮到易用和低耦合,保證高度的協做。測試

1.2 底層機制

將一個狀態會發生改變的對象定義成主題,全部的依賴對象定義爲觀察者。創建主題接口,對象使用該接口註冊爲觀察者,或者把本身從觀察者中刪除。定義一個具體的主題類實現主題接口。創建觀察者接口,聲明更新狀態的方法,具體的觀察者都要實現此接口。當主題狀態發生改變時,經過觀察者接口將改變發送給具體的觀察者。this

1.3實現方式

一、觀察者模式必須包含兩個角色:主題和觀察者。業務數據是主題,用戶界面是觀察者。當主題數據發生改變是會通知觀察者,觀察者作出的相應響應。spa

二、實現觀察者模式的方法不止一種,最爲直接的一種爲:註冊、通知、撤銷。設計

1.4體現的設計原則

一、爲了交互對象之間的鬆耦合設計而努力。3d

二、找出程序中會變化的方面,而後將其和固定不變的方面相分離。server

三、針對接口編程,不針對實現編程。對象

四、多用組合,少用繼承。blog

1.5優缺點

優勢:

1、觀察者模式提供了一種對象設計,讓主題和觀察者之間鬆耦合。

2、創建了一套觸發機制。

缺點:

一、若是主題有不少的直接或間接的觀察者,將改變通知到全部的觀察者須要花費很長時間。

二、主題不知道觀察者得細節,只知道觀察者實現了主題的接口。

三、若是主題和觀察之間存在循環依賴,可會致使系統崩潰。

1.6 類圖

1.7 案例:氣象觀測站

簡介:創建一個應用,利用WeatherData對象取得數據,而且更新三個佈告板:目前情況,氣象統計和天氣預報。

1.7.1 類圖

1.7.2代碼邏輯

 1、首先咱們從接口開始創建,創建主題接口和觀察者接口,以及界面顯示接口。

 2、在weatherData中實現主題接口。

 3、創建佈告板。

 4、編寫測試程序。

1.7.3代碼實現

一、實現主題接口()

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);//數據更新

}

三、實現佈告板接口

public interface DisplayElement {

public void display();//顯示界面

}

 

四、完成WeatherData類實現主題接口

public class WeatherDate implements Subject {

private ArrayList observers;//記錄觀察者,ArrayList是在構造器中創建的。

private float temperature;

private float humidity;

private float pressure;

 

public WeatherDate() {

observers = new ArrayList();

}

//subject接口的實現

//註冊觀察者

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 (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();

}

}

五、創建顯示當前觀測值得佈告板

public class CurrentConditionsDisplay implements Observer,DisplayElement{

 

private float temperature;

private float humidity;

private Subject weatherDate;

 

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");

}

 

public CurrentConditionsDisplay(Subject weatherDate){

this.weatherDate=weatherDate;

weatherDate.registerObserver(this);

}

}

六、創建根據氣壓計顯示天氣預報

 

public class ForecastDisplay implements Observer, DisplayElement {

private float currentPressure = 29.92f;  

private float lastPressure;

private WeatherDate weatherData;

 

public ForecastDisplay(WeatherDate 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("Improving weather on the way!");

} else if (currentPressure == lastPressure) {

System.out.println("More of the same");

} else if (currentPressure < lastPressure) {

System.out.println("Watch out for cooler, rainy weather");

}

  }

}

七、創建跟蹤最小值,平均值,最大的觀測值並顯示它們的佈告板。

 

public class StatisticsDisplay implements Observer, DisplayElement {

private float maxTemp = 0.0f;

private float minTemp = 200;

private float tempSum= 0.0f;

private int numReadings;

private WeatherDate weatherData;

 

public StatisticsDisplay(WeatherDate weatherData) {

this.weatherData = weatherData;

 weatherData.registerObserver(this);

}

 

public void update(float temp, float humidity, float pressure) {

tempSum += temp;

numReadings++;

 

if (temp > maxTemp) {

maxTemp = temp;

}

 

if (temp < minTemp) {

minTemp = temp;

}

 

display();

}

 

public void display() {

System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings)

+ "/" + maxTemp + "/" + minTemp);

}

}

 

八、創建測試類

 

public class WeatherSation {

 

public static void main(String[] args) {

WeatherDate weatherDate=new WeatherDate();

CurrentConditionsDisplay currentDisplay=new CurrentConditionsDisplay(weatherDate);

StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherDate);

ForecastDisplay forecastDisplay=new ForecastDisplay(weatherDate);

 

weatherDate.setMeasurements(80, 65, 30.4f);

weatherDate.setMeasurements(82, 70, 29.2f);

weatherDate.setMeasurements(78, 90, 29.2f);

 

}

 

}

九、運行結果

1.8思惟拓展

1java內置的觀察者模式。java.util包內包含最基本的Observer接口與Observable類,利用java內置的觀察者模式咱們只須要擴展(繼承)Observable,並告訴它什麼時候該通知觀察者,剩下的API會幫咱們作好。

1.9應用場景

一、有多個子類共有的方法,且邏輯相同。

二、存在數據實時更新的狀況下。

三、監聽器

2.1應用實例

一、報紙和雜誌的訂閱。

二、拍賣會上,拍賣師觀察最高標價,而後,通知其餘競價者。

三、護士將患者天天的檢查數據告知患者。

2.2注意事項

一、有多個觀察者時,不能夠依賴特定的通知次序。(緣由:一旦觀察者的實現有所改變,通知的次序就會改變,極可能會產生錯誤的結果。)

二、Java中已經有了對觀察者模式支持的類,包括通用的java.util.Observable

三、避免循環引用。

2.裝飾者模式

1.1定義

裝飾者模式:動態地將責任附加到對象身上。若要擴展功能,裝飾者提供了比繼承更有彈性·的替代方案。

1.2 底層機制

利用組合和委託實如今運行時具備繼承的效果。咱們將裝飾者和組件進行組合,就是在加入新的行爲,裝飾者和組件將更有彈性的加以混合和匹配。

1.3體現的設計原則

1、類應該對擴展開放,對修改關閉。

1.4優缺點

優勢:

1、經過動態組合對象,能夠寫新的代碼添加新的功能,而無需修改現有代碼。

2、裝飾類和被裝飾類能夠獨立發展,不會相互耦合,裝飾模式是繼承的一個替代模式,裝飾模式能夠動態擴展一個實現類的功能。

3、能夠透明的插入裝飾者,客戶程序甚至不須要知道在和裝飾者打交道。

缺點:

一、多層修飾比較複雜。

二、遵循開放-關閉原則,一般會引入新的抽象層次,增長代碼的複雜度。

三、有些代碼會依賴特定的類型,而這樣的代碼一導入裝飾者,就出現錯誤。

四、採用裝飾者在實例化組件時,將增長代碼的複雜度,一旦使用裝飾者模式,不僅須要實例化組件,還要把此組件包裝進裝飾者中。天曉得有幾個組件。

 
   

1.5類圖

 

1.6案例:星巴克咖啡

 1.6.1類圖

1.6.2代碼邏輯

一、拿一個咖啡(DarkRoast)對象

二、以摩卡(Mocha)對象裝飾它

三、以奶泡(Whip)對象修飾它

四、調用cost()方法。並依賴委託將調料的價錢加上去

1.6.3代碼實現

一、實現Beverage類,其爲一個抽象類。

 

public abstract class Beverage {

String description = "Unknown Beverage";

  

public String getDescription() {

return description;

}

 

public abstract double cost();

}

 

二、實現調料CondimentDecorator抽象類,即:裝飾者類

 

public abstract class CondimentDecorator extends Beverage {

public abstract String getDescription();

}

 

三、寫具體組件,飲料類的代碼

濃縮咖啡

public class Espresso extends Beverage {

  

public Espresso() {

description = "Espresso";

}

public double cost() {

return 1.99;

}

}

綜合咖啡:

public class HouseBlend extends Beverage {

public HouseBlend() {

description = "House Blend Coffee";

}

 

public double cost() {

return .89;

}

}

四、編寫調料類代碼,即具體的裝飾者類

摩卡:

public class Mocha extends CondimentDecorator {

Beverage beverage;//用一個實例變量記錄被裝飾者

    //將被裝飾者記錄到實例變量中

public Mocha(Beverage beverage) {

this.beverage = beverage;

}

 

public String getDescription() {

return beverage.getDescription() + ", Mocha";

}

 

public double cost() {

return .20 + beverage.cost();

}

}

奶泡:

public class Whip extends CondimentDecorator {

Beverage beverage;

 

public Whip(Beverage beverage) {

this.beverage = beverage;

}

 

public String getDescription() {

return beverage.getDescription() + ", Whip";

}

 

public double cost() {

return .10 + beverage.cost();

}

}

五、編寫測試代碼:

public class StarbuzzCoffee {

 

public static void main(String args[]) {

Beverage beverage = new Espresso();

System.out.println(beverage.getDescription()

+ " $" + beverage.cost());

 

Beverage beverage2 = new DarkRoast();

beverage2 = new Mocha(beverage2);

beverage2 = new Mocha(beverage2);

beverage2 = new Whip(beverage2);

System.out.println(beverage2.getDescription()

+ " $" + beverage2.cost());

 

Beverage beverage3 = new HouseBlend();

beverage3 = new Mocha(beverage3);

beverage3 = new Whip(beverage3);

System.out.println(beverage3.getDescription()

+ " $" + beverage3.cost());

}

}

 

運行結果

相關文章
相關標籤/搜索