重溫《深入淺出設計模式》觀察者模式 (Book Review of Head First Design Pattern, Observer Pattern)
2020-06-29
設計模式不會讓直觀上開發速度變得更快,反而在開發時會需要夠多的時間進行思考,然後才動手。但它的效益應該是當要維護、要擴充時,才能夠體現的,因為有良好的設計模式,程式碼的架構是充滿彈性且易於回應需求的,相較快速完成的架構可能缺少擴充的彈性,在面對需求時只能砍掉重練,或者是提心吊膽的修改程式碼害怕牽一髮動全身。無疑地如果在有需求變動的情境,使用設計模式先苦後甘是最好的選擇。
觀察者模式
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
範例是以實作 IOT (Subject) 裝置更新數據後,通知訂閱該數據的裝置 (Observer) 根據變動的數據做出更新。
Before Pattern
在模式之前直觀的做法就是將更新的數據依序傳入到觀察者中,但這樣的做法會有高耦合的問題,因為有新的觀察裝置加入 WeatherData 的程式碼就必須擴充改寫。
public class WeatherData
{
public void measurementsChanged()
{
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure();
currentConditionsDisplay.update(temp, humidity, pressure);
statisticsDiDisplay.update(temp, humidity, pressure);
forecastDisplay.update(temp, humidity, pressure);
}
}
After Pattern
觀察者模式首先定義 Subject Interface 與 Observer Interface,在關係上 Subject 擁有多個 Observer,並且 Subject 擁有 add, remove 方法來增減 Observer Instance,當 Subject 改變了,相依它的觀察者就會被通知並且被更新,通知及更新的動作是由 Subject 來執行的。
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();
}
實踐介面
public class WeatherData : Subject
{
private readonly 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.removeAt(i);
}
}
}
public void notifyObservers()
{
foreach (var observer in observers)
{
observer.update(temperature, humidity, pressure);
}
}
public setMeasutrements(float temperature, float humidity, float pressure)
{
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
notifyObservers();
}
實作一項觀察者類別
public class CurrentConditionsDisplay : Observer, DisplayElement
{
private float temperature;
private float humidity;
private float weatherData;
public CurrentConditionsDisplay(Subject weatherData)
{
this.weatherData = weatherData;
weatherData.registerObserver(this); // 建構時自動向 Subject 註冊
}
public void update(float temperature, float humidity, float pressure)
{
// 各觀察者類別獨特之處表現在不同的 update 邏輯上
this.temprature = temprature;
this.humidity = humidity;
display();
}
public void display()
{
...
}
}
流程測試
public class WeatherStation{
pbulic static void main(String[] args)
{
WeatherData weatherData = new WeatherData();
CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
weatherData.setMeasurements(85, 68, 36);
}
}
優點
- Subject 只知道 Observer 有實踐特定介面
- 任何時候都可以加入新的 Observer
- 有新類別的 Observer 出現時,不需要修改 Subject 的程式碼
- Subject 與 Observer 可以個別在不同的情境需求 Reuse
- 改變 Subject 與 Observer 不會影響到對方
小結
✔️將變動的部分封裝起來
- Observer 種類是會變動的
- Sbuject 的邏輯是會變動的
✔️多用合成,少用繼承
- Sbuject 擁有多個 Observer
- Observer 實作擁有一個 Sbuject 實作
✔️針對介面撰寫,而非針對實踐方式撰寫。
- 演算邏輯分別跟著 Subject 及 Observer 類別
✔️讓需要互動的物件之間的關係鬆綁
- 原本兩者 Subject 及 Observer 有高耦合,拆分之後自由奔放卻又合作無間