漫畫:設計模式中的 “觀察者模式”

—————  第二天  —————


————————————


場景1:遊戲操作界面

在一個小遊戲中,包含一個簡單的操作界面,界面上有兩個按鈕:道具和魔法。

如果點擊“道具”按鈕,遊戲裏的主角會使用道具;如果點擊“魔法”按鈕,遊戲裏的主角會使用魔法。

如何讓主角實時接收到點擊按鈕的事件,並做出相應的行動呢?

場景2:遊戲迷宮

同樣在這個小遊戲裏,有一個迷宮,迷宮裏有怪物、陷阱和寶物。

一旦主角移動到怪物的有效範圍,怪物會襲擊主角;主角移動到陷阱的有效範圍,陷阱會困住主角;主角移動到寶物的有效範圍,寶物會爲主角加血。

如何讓主角移動的事件被怪物、陷阱、道具感知到,並做出正確的反應?

public class Hero {
    //怪物
    Monster monster;
    //陷阱
    Trap trap;
    //寶物
    Treasure treasure;

    public void move(){
        System.out.println("主角向前移動");
        //主角移動時,分別通知怪物、陷阱和寶物對象
        monster.update();
        trap.update();
        treasure.update();
    }
}


在上面的UML圖中,主要有兩組實體對象,一組是觀察者,一組是被觀察者。所有的觀察者,都實現了Observer接口;所有的被觀察者,都繼承自Subject抽象類。

Subject類的成員OberverList,存儲着已註冊的觀察者,當事件發生時,會通知列表中的所有觀察者。需要注意的是,OberverList所依賴的是抽象的Observer接口,這樣就避免了觀察者與被觀察者的緊耦合。

//觀察者
public interface Observer {
    public void update();
}

//被觀察者
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();
        }
    }
}

//怪物
public class Monster implements Observer {

    @Override
    public void update() {
        if(inRange()){
            System.out.println("怪物 對主角攻擊!");
        }
    }

    private boolean inRange(){
        //判斷主角是否在自己的影響範圍內,這裏忽略細節,直接返回true
        return true;
    }
}

//陷阱
public class Trap 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;
    }
}

上面代碼中,每一個具體觀察者類都實現了update方法,這是事件觸發的回調方法,包含了具體觀察者對事件的不同反應。

public class Hero extends Subject{
    void move(){
        System.out.println("主角向前移動");
        notifyObservers();
    }
}


當主角移動時,通知所有已註冊的觀察者,執行具體觀察者各自的update方法。

public class Client {
    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();
    }
}

代碼輸出如下:

主角向前移動

怪物 對主角攻擊!

陷阱 困住主角!

寶物 爲主角加血!


推薦閱讀:

還在使用if else寫代碼?試試 “策略模式” 吧!

喜歡本文的朋友們,歡迎長按下圖關注公衆號程序員小灰,收看更多精彩內容

給個[在看],是對小灰最大的支持!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章