Observer 觀察者模式
假設有個需求
提供一個佈告欄, 分別提供 blog與forum頻道的資訊
1.希望會自動更新所有佈告欄
2.要容易增加新的佈告欄
或許我們可以這樣寫
//當資料有更新時
public class BulletinData{
public void update(){
String title = getTitle(); --> 也許直接去db撈, 或者call api
String content = getContent();
BulletinBlog.update(title, content);
BulletinForum.update(title, content);
}
}
public class BulletinBlog{
public void update();
public void display();
}
public class BulletinForum{
public void update();
}
觀察者模式就是 主題與觀察者一對多的關係
以出版社為例
出版者 + 訂閱者 = Observer模式
subject <-- 1 對多 --> observer
或許可以這樣寫
public interface ISubject{
public void registerObserver(IObserver o);
public void removeObserver(IObserver o);
public void notifyObserver();
}
public interface IObserver{
//這裡把這些field值寫死了, 不好
public void update(String title, String content);
}
public interface IDisplay{
public void display();
}
//資料來源的BulletinData當作subject
public class BulletinData implements ISubject{
private List lobserver;
private String title;
private String content;
public BulletinData(){
lobserver = new ArrayList();
}
public void registerObserver(IObserver o){
lobserver.add(o);
}
public void removeObserver(IObserver o){
lobserver.remove(o);
}
public void notifyObserver(){
for (int i = 0; i < lobserver.size(); i++) {
Observer observer = (Observer)lobserver.get(i);
observer.update(this.title, this.content);
}
}
public void update(){
notifyObserver();
}
public void setNewData(String title, String content){
this.title = title;
this.content = content;
this.update();
}
}
public class BulletinBlog implements IObserver, IDisplay{
private String title;
private String content;
private ISubject bulletinData;
public BulletinBlog(ISubject BulletinData){
this.bulletinData = bulletinData;
BulletinData.registerObserver(this); --> 註冊成為觀察者
}
public void update(String title, String content){
this.title = title;
this.content = content;
display();
}
public void display(){}
}
public class BulletinForum implements IObserver, IDisplay{
...
}
public class Demo {
public static void main(String[] args) {
BulletinData bulletinData = new BulletinData();
BulletinBlog bulletinBlog = new BulletinBlog(bulletinData);
BulletinForum bulletinForum= new BulletinForum(bulletinData);
//當資料有更新時, 也許直接去db撈, 或者call api
String title = "new title";
String content = "new content";
bulletinData.update(title, content); --> 會通知所有有註冊的觀察者了
}
}
subject依賴的是一個observer的interface, 所以subject不用管有多少class去implements
以上的作法,
java.util 有提供Observer模式
java.util.Observer 這是interface, 讓我們implents觀察者
java.util.Observable 可以去extends Observable class, 再去增加取得新資料的方法
上方的例子, 主動權是在subject, 都是由subject去把資料推給觀察者,
而java.util提供的Observer可以做到由觀察者自己去拉資料
下方的code是由java.util提供方式來實作
head first design pattern一書copy貼上的, 請參考用
import java.util.Observable;
import java.util.Observer;
public class WeatherData extends Observable {
private float temperature;
private float humidity;
private float pressure;
public WeatherData() { }
public void measurementsChanged() {
setChanged();
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;
}
}
import java.util.Observable;
import java.util.Observer;
public class CurrentConditionsDisplay implements Observer, DisplayElement {
Observable observable;
private float temperature;
private float humidity;
public CurrentConditionsDisplay(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
public void update(Observable obs, Object arg) {
if (obs instanceof WeatherData) {
WeatherData weatherData = (WeatherData)obs;
this.temperature = weatherData.getTemperature();
this.humidity = weatherData.getHumidity();
display();
}
}
public void display() {
System.out.println("Current conditions: " + temperature
+ "F degrees and " + humidity + "% humidity");
}
}
名言??
松耦合 --> 當二個object可以交互, 但又不清楚彼此的細節