觀察者模式詳解


觀察者模式

1.1觀察者模式概述

觀察者模式(Observer Pattern)又叫做發布-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式。定義了一種一對多的依賴關系,一個主題對象可被多個觀察者對象同時監聽,使得每當主題對象狀態變化時,所有依賴它的對象都會得到通知並自動更新,屬於行為型設計模式。
觀察者的核心是將觀察者與被觀察者解耦,以類似消息/廣播發送的機制聯動兩者,使被觀察者的變動能通知到感興趣的觀察者們,從而做出相應的響應。

1.2觀察者模式的應用場景

觀察者模式一般會應用到App的鬧鍾設置,消息的廣播通知,郵件通知等。

1.3觀察者模式的通用寫法

/**
 * 抽象主題者
 *
 * @author yml
 * @since 2022/4/13 11:24
 */
public interface ISubject<T> {
    boolean attach(IObserver<T> observer);

    boolean detach(IObserver<T> observer);

    void notify(T event);
}
/**
 * 抽象觀察者
 *
 * @author yml
 * @since 2022/4/13 11:21
 */
public interface IObserver<T> {
    void update(T event);
}
/**
 * 具體主題者
 *
 * @author yml
 * @since 2022/4/13 14:46
 */
public class ConcreteSubject<T> implements ISubject<T> {

    private List<IObserver<T>> observers = new ArrayList<IObserver<T>>();
    @Override
    public boolean attach(IObserver<T> observer) {
        return !this.observers.contains(observer) && this.observers.add(observer);
    }

    @Override
    public boolean detach(IObserver<T> observer) {
        return this.observers.remove(observer);
    }

    @Override
    public void notify(T event) {
        for (IObserver<T> observer:this.observers) {
            observer.update(event);
        }
    }
}
/**
 * 具體觀察者
 *
 * @author yml
 * @since 2022/4/13 14:43
 */
public class ConcreteObserver<T> implements IObserver<T> {

    @Override
    public void update(T event) {
        System.out.println("receive event: " + event);
    }
}
/**
 * @Author:yml
 * @Data:2022/4/13
 */
public class ClientTest {
    public static void main(String[] args) {
        // 被觀察者
        ISubject<String> subject = new ConcreteSubject<>();
        // 觀察者
        IObserver<String> observer = new ConcreteObserver<>();
        // 將觀察者注冊
        subject.attach(observer);
        // 被觀察者通知觀察者
        subject.notify("hello");
    }
}

繪制出觀察者uml圖:
觀察者模式
由上圖可以看出,觀察者模式主要包含4個角色:

  • 抽象主題(ISubject):指被觀察的對象。該角色是一個抽象類或接口,定義了增加、刪除、通知觀察者對象的方法。
  • 具體主題(ConcreteSubject):具體被觀察者,當其內部狀態發生變化時,會通知已注冊的觀察者。
  • 抽象觀察者(IObserver):定義了響應通知的更新方法。
  • 具體觀察者(ConcreteObserver):當得到狀態更新通知時,會自動做出響應。

2.1觀察者模式的具體實現

2.1.1 JDK中的觀察者模式

模擬一個場景,在線教育平台,當某個學生在線上提出問題並指出對應指導老師回答,指定老師則會收到相應郵件通知

import java.util.Observable;

/**
 * 被觀察者
 *
 * @author yml
 * @since 2022/4/13 15:48
 */
public class GPer extends Observable {
    private String name = "GPer 平台";

    private static GPer gPer = null;

    private GPer(){}

    public static GPer newInstance(){
        if (null == gPer){
            gPer = new GPer();
        }
        return gPer;
    }

    public String getName() {
        return name;
    }

    public void publishQuestion(Question question){
        System.out.println(question.getUserName()+":"+question.getContent());
        setChanged();
        notifyObservers(question);
    }
}
/**
 * @Author:yml
 * @Data:2022/4/13
 */
public class Question {
    private String userName;

    private String content;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}
import java.util.Observable;
import java.util.Observer;

/**
 * @Author:yml
 * @Data:2022/4/13
 */
public class Teacher implements Observer {
    private String name;

    public Teacher(String name){
        this.name = name;
    }
    @Override
    public void update(Observable o, Object arg) {
        GPer gPer = (GPer) o;
        Question question = (Question) arg;
        System.out.println("===========================================");
        System.out.println(this.name + "老師,你好 \n" + "您收到一條來自" + gPer.getName() + "\n" + "提問者:" + question.getUserName() +
                "\n" + "問題描述:" + question.getContent());
    }
}
/**
 * @Author:yml
 * @Data:2022/4/13
 */
public class ClientTest {
    public static void main(String[] args) {
        GPer gPer = GPer.newInstance();
        Teacher tom = new Teacher("Tom");
        Teacher jerry = new Teacher("Jerry");

        gPer.addObserver(tom);
        gPer.addObserver(jerry);

        // 用戶操作
        Question question = new Question();
        question.setUserName("amazing");
        question.setContent("如何學好JAVA");

        gPer.publishQuestion(question);
    }
}

2.1.2 基於Guava API實現觀察者模式

需要引入maven依賴包

<dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>20.0</version>
</dependency>
import com.google.common.eventbus.EventBus;
import org.example.observereg.Question;

/**
 * @Author:yml
 * @Data:2022/4/13
 */
public class GuavaBus {
    public static final EventBus bus = new EventBus();

    public static void register(GuavaEvent event){
        if (event == null){
            return;
        }
        bus.register(event);
    }

    public static void question(Question question){
        bus.post(question);
    }
}
import com.google.common.eventbus.Subscribe;
import org.example.observereg.Question;

/**
 * @Author:yml
 * @Data:2022/4/13
 */
public class GuavaEvent {

    @Subscribe
    public void subscribe(Question question){
        System.out.println(question.getUserName()+":"+question.getContent());
    }
}
import org.example.observereg.Question;

/**
 * @Author:yml
 * @Data:2022/4/13
 */
public class GuavaEventTest {
    public static void main(String[] args) {
        GuavaEvent guavaEvent = new GuavaEvent();
        GuavaBus.register(guavaEvent);
        Question question = new Question();
        question.setUserName("amazing");
        question.setContent("如何學好JAVA");
        GuavaBus.question(question);

    }
}

3.1 觀察這模式在Spring源碼中的應用

Spring中的事件機制,Spring中的ContextLoaderListener實現ServletContextListener,ServletContextListener又繼承JDK的EventListener,實現事件監聽。

3.2 觀察者模式的優缺點

  • 優點:
    1.觀察者和被觀察者都是松耦合(抽象耦合),符合依賴倒置原則。
    2.分離了表示層(觀察者)和數據邏輯層(被觀察者者),並且建立了一套觸發機制,使得數據的變化可以響應到多個表示層。
    3.實現了一對多的通信機制,支持事件注冊機制,支持興趣分發機制,當被觀察者觸發時間是,只有訂閱的觀察者可以接受通知。
  • 缺點
    1.如果觀察者過多,則事件通知會耗時較長。
    2.事件通知為線性,可能出現阻塞。
    3.觀察者和被觀察者可能存在循環依賴,可能造成循環調用,導致系統崩潰。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM