設計模式學習筆記(3):觀察者模式(2)
阿新 • • 發佈:2019-02-14
上一篇的觀察者模式的筆記中提到的例子,只是Subject把自身狀態“推”給Observer的方式。
有些實現中,會需要將改變前和改變後的值都放在通知裡,這就沒法用get了。
//通知觀察者資訊發生改變了 @Override public void notifyObservers() { for (int i = 0;i < observers.size();i++){ Observer observer = (Observer) observers.get(i); observer.update(travelerNum,terminal,information); } } //更新旅行資訊 public void setTravelInformation(int travelerNum,String terminal,String information){ this.travelerNum = travelerNum; this.information = information; this.terminal = terminal; notifyObservers(); }
從程式碼中可以看出,只要Subject發生改變,Observer就會被動接受全部資訊,並且不得不接受。
這種模式的優勢在於:Observer不用呼叫多次,才收集全所有需要的訊息。
同時這種模式會造成一定的麻煩:
1、Observer可能並不需要所有的訊息;
2、在Subject需要擴充套件時,需要更新每一個傳送給Observer的訊息 。
這時我們可以在Subject中提供一些公開的getter方法,讓Observer自己索取所需要的資訊。
在這裡我們還是選取旅行者的例子。
Subject/Observable介面:
Observer介面:public interface Observable { public void registerObserver(Observer o); public void removeObserver(Observer o); public void notifyObservers(); }
public interface PrepareTravel {
void doPrepare();
}
用來展示的介面PrepareTravel():
public interface PrepareTravel {
void doPrepare();
}
Organizer去實現Obserable介面:
public class Organizer implements Observable { //標記Subject狀態是否發生改變 private boolean changed; //旅行者人數 private int travelerNum; //目的地 private String terminal; //相關資訊 private String information; //用於存放observers的陣列 private ArrayList observers; //構造器 public Organizer(){ //初始化observers,否則會報空指標 observers = new ArrayList(); //標記預設為false,未發生改變 changed = false; } //註冊observer @Override public void registerObserver(Observer o) { observers.add(o); } //刪除observer @Override public void removeObserver(Observer o) { int i = observers.indexOf(o); if (i >= 0){ observers.remove(i); } } //通知observer @Override public void notifyObservers() { if (changed){ for (int i = 0;i < observers.size();i++){ Observer observer = (Observer) observers.get(i); observer.update(this); } changed = false; } } //改變發生 public void setInformationChanged(int travelerNum,String terminal,String information){ changed = true; this.travelerNum = travelerNum; this.terminal = terminal; this.information = information; notifyObservers(); } //公共的getter方法供observer去“拉”所需要的資訊 public int getTravelerNum() { return travelerNum; } public String getTerminal() { return terminal; } public String getInformation() { return information; } }
在這裡我們引進了changed標記,使得更新觀察者時,擁有更多的彈性。在這個場景中,我們並不想每加一個人都通知觀察者Traveler,我們想變更達到10人才通知觀察者Traveler。我們可以新增條件:
//改變發生
public void setInformationChanged(int travelerNum,String terminal,String information){
if (travelerNum - this.travelerNum >= 10){
changed = true;
this.travelerNum = travelerNum;
this.terminal = terminal;
this.information = information;
notifyObservers();
}
}
Traveler去實現Observer介面:
public class Traveler implements Observer,PrepareTravel {
//Subject
Observable observable;
//旅客人數
private int travelerNum;
//目的地
private String terminal;
//相關資訊
private String information;
//構造器
public Traveler(Observable observable){
this.observable = observable;
//註冊
observable.registerObserver(this);
}
//更新自己所需要的資訊
@Override
public void update(Observable o) {
if (o instanceof Organizer){
Organizer organizer = (Organizer) o;
//可以“拉”自身所需要的資訊
this.travelerNum = organizer.getTravelerNum();
this.terminal = organizer.getTerminal();
this.information = organizer.getInformation();
doPrepare();
}
}
//展示
@Override
public void doPrepare() {
System.out.println("我已知悉這次旅行有"+travelerNum+"人,前往"+terminal+",其他相關資訊有"+information);
}
}
模擬旅行:
public class TravelSimulator {
public static void main(String[] args) {
Organizer organizer = new Organizer();
Traveler traveler = new Traveler(organizer);
organizer.setInformationChanged(10,"九華山","安徽");
organizer.setInformationChanged(11,"黃山","安徽");
organizer.setInformationChanged(30,"峨眉山","四川");
}
}
新增changed條件前結果:
新增changed條件後結果:
《HeadFirst》中,推的方式被認為更正確(請教了大佬):有些實現中,會需要將改變前和改變後的值都放在通知裡,這就沒法用get了。