參考這篇文章,很生動:https://zhuanlan.zhihu.com/p/158537313
有點像發佈訂閱,有不同之處,但是要實現的功能很像
舉了一個遊戲的例子
Hero類走格子,有怪物類,寶物類,走到格子時需要判斷是否進入這些格子並且產生效果。
傳統想法之一(拉取):怪物類隔100ms判斷一次,但這樣一直空轉
傳統想法之二(推送):把幾個類都寫到Hero類中了,每次Hero類移動時做判斷
觀察者模式:
觀察者:實現Observer接口(怪物類,寶物類),主要功能就是update
被觀察者:繼承Subject抽象類(Hero),有一個ObserverList,當需要更新時,notify觀察者
邏輯:被觀察者移動--notify觀察者--觀察者更新
代碼:
觀察者
//觀察者 接口 public interface Observer { public void update(); } //怪物 public class Monster implements Observer { @Override public void update() { if(inRange()){ System.out.println("怪物 對主角攻擊!"); } } private boolean inRange(){ //判斷主角是否在自己的影響範圍內,這裏忽略細節,直接返回true return true; } } //寶物 邏輯一樣 public class Treasure implements Observer { @Override public void update() { if(inRange()){ System.out.println("寶物 爲主角加血!"); } } private boolean inRange(){ //判斷主角是否在自己的影響範圍內,這裏忽略細節,直接返回true return true; } }
被觀察者:
//被觀察者 abstract public class Subject { private List<Observer> observerList = new ArrayList<Observer>(); public void attachObserver(Observer observer) { observerList.add(observer); }//增加觀察者 public void detachObserver(Observer observer){ observerList.remove(observer); }//刪除觀察者 public void notifyObservers(){ for (Observer observer: observerList){ observer.update(); } } } //實際被觀察者,繼承Subject類 public class Hero extends Subject{ void move(){ System.out.println("主角向前移動"); notifyObservers(); } }
main函數中代碼
public static void main(String[] args) { //初始化對象 Hero hero = new Hero(); Monster monster = new Monster(); Trap trap = new Trap(); Treasure treasure = new Treasure(); //註冊觀察者 hero.attachObserver(monster); hero.attachObserver(trap); hero.attachObserver(treasure); //移動事件 hero.move(); } }
如果在被觀察者之間有循環依賴的話,被觀察者會觸發它們之間進行循環調用,導致系統崩潰。在使用觀察者模式是要特別注意這一點。
和發佈訂閱的區別