慨念:定義了對象之間的一對多的依賴,這樣一來,當一個對象改變狀態時,它的全部依賴者都會收到通知並自動更新。java
即:主題和觀察者定義了一對多的關係,觀察者依賴於主題,只要主題發生變化,觀察者就會被通知。編程
目的:一個對象狀態改變給其餘對象通知的問題,並且要考慮到易用和低耦合,保證高度的協做。測試
將一個狀態會發生改變的對象定義成主題,全部的依賴對象定義爲觀察者。創建主題接口,對象使用該接口註冊爲觀察者,或者把本身從觀察者中刪除。定義一個具體的主題類實現主題接口。創建觀察者接口,聲明更新狀態的方法,具體的觀察者都要實現此接口。當主題狀態發生改變時,經過觀察者接口將改變發送給具體的觀察者。this
一、觀察者模式必須包含兩個角色:主題和觀察者。業務數據是主題,用戶界面是觀察者。當主題數據發生改變是會通知觀察者,觀察者作出的相應響應。spa
二、實現觀察者模式的方法不止一種,最爲直接的一種爲:註冊、通知、撤銷。設計
一、爲了交互對象之間的鬆耦合設計而努力。3d
二、找出程序中會變化的方面,而後將其和固定不變的方面相分離。server
三、針對接口編程,不針對實現編程。對象
四、多用組合,少用繼承。blog
優勢:
1、觀察者模式提供了一種對象設計,讓主題和觀察者之間鬆耦合。
2、創建了一套觸發機制。
缺點:
一、若是主題有不少的直接或間接的觀察者,將改變通知到全部的觀察者須要花費很長時間。
二、主題不知道觀察者得細節,只知道觀察者實現了主題的接口。
三、若是主題和觀察之間存在循環依賴,可會致使系統崩潰。
1.6 類圖
簡介:創建一個應用,利用WeatherData對象取得數據,而且更新三個佈告板:目前情況,氣象統計和天氣預報。
1、首先咱們從接口開始創建,創建主題接口和觀察者接口,以及界面顯示接口。
2、在weatherData中實現主題接口。
3、創建佈告板。
4、編寫測試程序。
一、實現主題接口()
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、java內置的觀察者模式。java.util包內包含最基本的Observer接口與Observable類,利用java內置的觀察者模式咱們只須要擴展(繼承)Observable,並告訴它什麼時候該通知觀察者,剩下的API會幫咱們作好。
一、有多個子類共有的方法,且邏輯相同。
二、存在數據實時更新的狀況下。
三、監聽器
一、報紙和雜誌的訂閱。
二、拍賣會上,拍賣師觀察最高標價,而後,通知其餘競價者。
三、護士將患者天天的檢查數據告知患者。
一、有多個觀察者時,不能夠依賴特定的通知次序。(緣由:一旦觀察者的實現有所改變,通知的次序就會改變,極可能會產生錯誤的結果。)
二、Java中已經有了對觀察者模式支持的類,包括通用的java.util.Observable。
三、避免循環引用。
裝飾者模式:動態地將責任附加到對象身上。若要擴展功能,裝飾者提供了比繼承更有彈性·的替代方案。
利用組合和委託實如今運行時具備繼承的效果。咱們將裝飾者和組件進行組合,就是在加入新的行爲,裝飾者和組件將更有彈性的加以混合和匹配。
1、類應該對擴展開放,對修改關閉。
優勢:
1、經過動態組合對象,能夠寫新的代碼添加新的功能,而無需修改現有代碼。
2、裝飾類和被裝飾類能夠獨立發展,不會相互耦合,裝飾模式是繼承的一個替代模式,裝飾模式能夠動態擴展一個實現類的功能。
3、能夠透明的插入裝飾者,客戶程序甚至不須要知道在和裝飾者打交道。
缺點:
一、多層修飾比較複雜。
二、遵循開放-關閉原則,一般會引入新的抽象層次,增長代碼的複雜度。
三、有些代碼會依賴特定的類型,而這樣的代碼一導入裝飾者,就出現錯誤。
四、採用裝飾者在實例化組件時,將增長代碼的複雜度,一旦使用裝飾者模式,不僅須要實例化組件,還要把此組件包裝進裝飾者中。天曉得有幾個組件。
一、拿一個咖啡(DarkRoast)對象
二、以摩卡(Mocha)對象裝飾它
三、以奶泡(Whip)對象修飾它
四、調用cost()方法。並依賴委託將調料的價錢加上去
一、實現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());
}
}
運行結果