設計模式之觀察者模式

0x01.定義與類型

  • 定義:定義了對象之間的一對多依賴,讓多個觀察者對象同時監聽某一個主題對象,當主題對象發生變化時,它的全部依賴者(觀察者)都會收到通知並更新。
  • 類型:行爲型
  • UML類圖

observer1.png

  • 示例代碼實現
/**
 * 被觀察實例抽象定義
 */
public abstract class Subject {

    /**
     * 觀察者數組
     */
    protected List<Observer> observers;

    /**
     * 添加一個觀察者
     * @param observer
     */
    public abstract void add(Observer observer);

    /**
     * 刪除一個觀察者
     * @param observer
     */
    public abstract void remove(Observer observer);


    /**
     * 通知觀察者
     */
    public abstract void notifyObserver();
}

/**
 * 觀察者接口
 */
public interface Observer {
    void response();
}

/**
 * 被觀察的實例
 */
public class ConcreteSubject extends Subject {

    public ConcreteSubject() {
        super.observers = new ArrayList<>();
    }

    @Override
    public void add(Observer observer) {
        this.observers.add(observer);
    }

    @Override
    public void remove(Observer observer) {
        this.observers.remove(observer);
    }

    @Override
    public void notifyObserver() {
        for (Observer observer : this.observers) {
            observer.response();
        }
    }
}

/**
 * 觀察者1
 */
public class ConcreteObserver1 implements Observer {
    @Override
    public void response() {
        System.out.println("通知觀察者1");
    }
}

/**
 * 觀察者2
 */
public class ConcreteObserver2 implements Observer {
    @Override
    public void response() {
        System.out.println("通知觀察者2");
    }
}
  • 測試與應用
/**
 * 測試與應用
 */
public class Test {

    public static void main(String[] args) {
        //建立實例
        Subject subject = new ConcreteSubject();

        //建立觀察者對象
        Observer observer1 = new ConcreteObserver1();
        Observer observer2 = new ConcreteObserver2();

        //添加觀察者對象
        subject.add(observer1);
        subject.add(observer2);

        //通知觀察者
        subject.notifyObserver();
    }
}
  • 輸出結果
通知觀察者1
通知觀察者2
  • 角色介紹:html

    • 抽象被觀察者角色(Subject):也就是一個抽象主題,它把全部對觀察者對象的引用保存在一個集合中,每一個主題均可以有任意數量的觀察者。抽象主題提供一個接口,能夠增長和刪除觀察者角色。通常用一個抽象類和接口來實現。
    • 觀察者角色接口(Observer):爲全部的具體觀察者定義一個接口,在獲得主題通知時更新本身。
    • 具體被觀察者角色(ConcreteSubject):也就是一個具體的主題,在集體主題的內部狀態改變時,全部登記過的觀察者發出通知。
    • 具體觀察者角色(ConcreteObserver):實現抽象觀察者角色所須要的更新接口,一邊使自己的狀態與主題的狀態相協調。

0x02.適用場景

  • 關聯行爲場景,創建一套觸發機制

0x03.優缺點

1.優勢

  • 觀察者和被觀察者之間創建一個抽象的耦合
  • 觀察者模式支持廣播通訊

2.缺點

  • 觀察者之間有過多的細節依賴,提升時間消耗以及程序複雜度
  • 使用要得當,要避免循環調用

0x04.代碼示例

使用jdk觀察者工具類實現,學生提出問題,多個老師接收問題。
  • 具體實現
/**
 * 觀察者
 */
public class Teacher implements Observer {

    private String teacherName;

    public Teacher(String teacherName) {
        this.teacherName = teacherName;
    }

    @Override
    public void update(Observable o, Object arg) {
        Course course = (Course) o;
        Question question = (Question) arg;

        System.out.println(teacherName + "老師的" + course.getCourseName()
                + "課程接收到一個" + question.getUsername()
                + "提出的" + question.getQuestionContent() + "問題");
    }
}

/**
 * 被觀察對象實體
 */
public class Course extends Observable {

    private String courseName;

    public Course(String courseName) {
        this.courseName = courseName;
    }

    public String getCourseName() {
        return courseName;
    }

    public void produceQuestion(Course course, Question question) {
        System.out.println(question.getUsername() + "在" + course.getCourseName() + "提交了一個問題。");
        setChanged();
        notifyObservers(question);
    }
}

/**
 * 問題實體
 */
public class Question {

    private String username;

    private String questionContent;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getQuestionContent() {
        return questionContent;
    }

    public void setQuestionContent(String questionContent) {
        this.questionContent = questionContent;
    }
}
  • 測試與應用
/**
 * 測試類
 */
public class Test {

    public static void main(String[] args) {
        Course course = new Course("Javas設計模式精講");
        Teacher teacher1 = new Teacher("Alpha");
        Teacher teacher2 = new Teacher("Beta");

        course.addObserver(teacher1);
        course.addObserver(teacher2);

        //業務邏輯代碼
        Question question = new Question();
        question.setUsername("K.O");
        question.setQuestionContent("Java的主函數如何編寫");

        course.produceQuestion(course, question);

    }
}
  • 輸出
K.O在Javas設計模式精講提交了一個問題。
Beta老師的Javas設計模式精講課程接收到一個K.O提出的Java的主函數如何編寫問題
Alpha老師的Javas設計模式精講課程接收到一個K.O提出的Java的主函數如何編寫問題
  • UML類圖

observer2.png

0x05.擴展

  • google guava中的@Subscribe註解
public class GuavaEvent {

    @Subscribe
    public void subscribe(String str)  {
        //業務邏輯
        System.out.println("執行subscribe方法,傳入的參數是:" + str);
    }

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

    public static void main(String[] args) {
        EventBus eventBus = new EventBus();
        GuavaEvent guavaEvent = new GuavaEvent();
        eventBus.register(guavaEvent);
        eventBus.post("post的內容");
    }
}
  • 輸出結果
執行subscribe方法,傳入的參數是:post的內容
  • EventBus會把消息發送到註冊的類帶有@Subscribe的方法中。使用guava能夠更容易的使用觀察者模式。

0x06.源碼中的觀察者模式

  • awt桌面程序,Event
  • Listener

0x07.源碼地址

0x08.推薦閱讀

相關文章
相關標籤/搜索