觀察者模式

定義

定義對象的一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新。

觀察者模式是行爲型模式之一。包含觀察者模式包括觀察者與被觀察者兩個基本元素,觀察者會註冊到被觀察者中,而被觀察者會保持和觀察者的對應關係。

如果被觀察的對象產生一些觀察者感興趣的變更,則觀察者會得到通知從而去執行相關的操作。

常見的觀察者模式

提到觀察者模式,首先會想到前端中常用的事件機制。例如爲dom元素綁定事件:

$(document).click(function (e) {
    console.dir(e);
});

這裏的匿名function就是觀察者,而document則是被觀察者,.click(function)則是觀察者註冊的過程,此後document發生click事件時,被觀察者則會收到通知,從而執行自己的邏輯console.dir(e)。

使用觀察者模式

現在我們使用Java來模擬觀察者模式。

先創建事件,包含事件源,通知目標,回調方法以及觸發者等屬性。

/**
 * 事件
 */
public class Event {

    private Object source; // 事件源
    private Object target; // 通知目標
    private Method callback; // 回調方法名稱
    private String trigger; // 觸發者
    private long time;

    public Event(Object target, Method callback) {
        this.target = target;
        this.callback = callback;
    }

    // 省略getter、setter
}

定義事件類型,此處定義添加、刪除兩種類型。

/**
 * 事件類型
 */
public enum EventType {
    /**
     * 添加
     */
    ADD,

    /**
     * 刪除
     */
    RMOVE
}

定義抽象被觀察者(主題),包含主題名稱,保持與觀察者引用映射的容器,以及事件觸發的方法。

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 抽象主題
 * 定義主題通用的容器、事件註冊及事件觸發
 */
public class AbstractSubject {

    protected String name;
    protected Map<Enum,Event> eventMap = new HashMap<>(); // 註冊容器

    public AbstractSubject() {}

    protected AbstractSubject(String name) {
        this.name = name;
    }

    /**
     * 事件註冊
     * @param eventType
     * @param target
     * @param callback
     */
    public void addLisenter(Enum eventType, Object target, Method callback) {
        eventMap.put(eventType, new Event(target, callback));
    }

    /**
     * 事件觸發
     * @param eventType
     */
    protected void trigger(Enum eventType) {
        Event event = this.eventMap.get(eventType);
        if(null == event) {
            return;
        }

        event.setSource(this);
        event.setTrigger(eventType.toString());
        event.setTime(System.currentTimeMillis());

        try {
            event.getCallback().invoke(event.getTarget(), event);
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }

    // 省略getter、setter
}

定義具體主題:

/**
 * 主題,被觀察者
 */
public class Subject extends AbstractSubject {

    public Subject(String name) {
        super.name = name;
    }

    public void add() {
        trigger(EventType.ADD);
    }

    public void remove() {
        trigger(EventType.RMOVE);
    }
}

定義觀察者,包含添加、刪除事件對應的回調方法。

/**
 * 觀察者
 */
public class Observer {

    public void onAdd(Event event) {
        System.out.println("收到" + event.getTrigger() + "的添加事件");
    }

    public void onRemove(Event event) {
        System.out.println("收到" + event.getTrigger() + "的刪除事件");
    }
}

測試類:

/**
 * 測試類
 */
public class Test {

    public static void main(String[] args) throws NoSuchMethodException {
        // 觀察者
        Observer observer = new Observer();

        // 觀察者收到通知後執行的方法
        Method onAdd = Observer.class.getMethod("onAdd", Event.class);
        Method onRemove = Observer.class.getMethod("onRemove", Event.class);

        // 被觀察者
        Subject subject = new Subject("目標1號");

        // 事件綁定
        subject.addLisenter(EventType.ADD, observer, onAdd);
        subject.addLisenter(EventType.RMOVE, observer, onRemove);

        // 被觀察者觸發事件
        subject.add();
        subject.remove();
    }
}

其中,得到觀察者和主題對象後,進行觀察者的註冊,然後主題執行動作觸發事件,這是整個流程。

如果需要做成主題和觀察者一對多的映射,則將Event的target改成集合類型,如下:

private List<Object> targets; // 通知目標
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章