觀察者模式 | Observer Pattern

Posted by Ian Tsai on Monday, October 5, 2020

本系列文章同步分享於IT邦幫忙第12屆鐵人賽


Design Pattern 系列文章導讀


Design Pattern可以說是開發上大家都會遇到的一個課題, 這系列文會從Design Principles、各種design pattern到最後的Anti-Patterns & Code Smells介紹下去,讓我們可以更了解各種pattern的使用時機與場合。 預計目標主題如下,若有哪部分不熟的章節可以直接點進去看
註:可以利用Online Java Compiler IDE

  1. 設計模式 - 入門篇
  2. 設計模式 - 原則篇 | Design Principles
  3. 設計模式 - 創建型模型篇 | Creational Patterns
  4. 設計模式 - 結構型模型篇 | Structural Patterns
  5. 設計模式 - 行為型模型篇 | Behavioural Patterns
  6. 設計模式 - 番外篇
  • 定義


多個物件之間存在著一對多的依賴關係,當一個物件發生改變時,所有跟他有關的物件都會被通知且更新。

現實生活中就有許多觀察者模式的案例,例如:訂閱youtuber,發布新影片就會接收到通知;加入Line官方帳號,會接收到優惠訊息…等等。

  • Observer Pattern 成員


name description
Subject 抽象目標,提供保存觀察者物件的集合以及增加觀察者物件的方法、刪除觀察者物件的方法以及通知所有觀察者的抽象方法。
ConcreteSubject 具體目標,實作抽象目標中的通知方法。具體目標內部發生改變時會通知所有加入的觀察者物件。
Observer 抽象觀察者,可以是抽象類別或是介面。含有更新自己的抽象方法。
ConcreteObserver 實作抽象觀察者,在目標更改狀態時接收通知並更改自身狀態。
  • Observer Pattern 實作


就拿現在很紅的Youtuber來做範例,Peeta葛格是一個Youtuber,我們訂閱了之後,就可以接收到他發片的通知,所以Peeta是目標,而我們是觀察者。廠商也訂閱了Peeta,他想知道Peeta發片時是不是葉配影片,所以廠商也是觀察者。

import java.util.*;

enum Type { // 建立列舉方便等等程式使用
    BUSINESS, VLOG
}

//抽象目標:分別有存放觀察者的集合以及增加、刪除、通知觀察者的方法
abstract class Subject {
    protected List<Observer> observers = new ArrayList<Observer>();   
    //增加觀察者
    public void add(Observer observer) {
        observers.add(observer);
    }    
    //刪除觀察者
    public void remove(Observer observer) {
        observers.remove(observer);
    }   
    public abstract void notifyObserver(Type type); //通知觀察者
}
//實體目標 Peeta葛格:實作抽象目標,通知觀察者的方法。
class YoutuberPeeta extends Subject {
    public void notifyObserver(Type type) {
        String videoType = null;
        switch(type){
            case BUSINESS:
                videoType = "葉配";
                break;
            case VLOG:
                videoType = "vlog";
                break;
        }
        System.out.println("Peeta 發布新" + videoType + "影片");
        System.out.println("--------------");       
       
        for(Object obs:observers) {
            ((Observer)obs).response(type);
        }
       
    }          
}

//抽象觀察者:定義接收到目標時的方法。
interface Observer {
    void response(Type type); //反應
}
//實體觀察者 粉絲
class Fans implements Observer {
    public void response(Type type) {
        switch(type){
            case BUSINESS:
                System.out.println("紛絲看到葉配表示傷心!");
                break;
            case VLOG:
                System.out.println("紛絲看到VLOG覺得有趣!");
                break;
        }
    }
}
//實體觀察者:廠商
class Vendor implements Observer {
    public void response(Type type) {
                switch(type){
            case BUSINESS:
                System.out.println("廠商看到葉配覺得開心!");
                break;
            case VLOG:
                System.out.println("廠商看到VLOG表示為啥不是葉配!");
                break;
        }
    }
}

// Client分別建立目標及兩個觀察者,而目標在發布新影片時會同時通知兩個觀察者
public class ObserverPattern {
    public static void main(String[] args) {
        Subject subject = new YoutuberPeeta();
        Observer obs1 = new Fans();
        Observer obs2 = new Vendor();
        subject.add(obs1);
        subject.add(obs2);
        subject.notifyObserver(Type.BUSINESS);
        System.out.println();    
        subject.notifyObserver(Type.VLOG);
    }
}

output

Peeta 發布新葉配影片
--------------
紛絲看到葉配表示傷心!
廠商看到葉配覺得開心!

Peeta 發布新vlog影片
--------------
紛絲看到VLOG覺得有趣!
廠商看到VLOG表示為啥不是葉配!
  • 小結


Observer Pattern的目標

多個物件之間存在著一對多的依賴關係,當一個物件發生改變時,所有跟他有關的物件都會被通知且更新。

Observer Pattern的成員
Subject:抽象目標,提供保存觀察者物件的集合以及增加觀察者物件的方法、刪除觀察者物件的方法以及通知所有觀察者的抽象方法。
ConcreteSubject:具體目標,實作抽象目標中的通知方法。具體目標內部發生改變時會通知所有加入的觀察者物件。
Observer:抽象觀察者,可以是抽象類別或是介面。含有更新自己的抽象方法。
ConcreteObserver:實作抽象觀察者,在目標更改狀態時接收通知並更改自身狀態。
Observer Pattern的優缺點
優點
1. 降低目標與觀察者間的耦合關係,兩者之間為抽象耦合關係。
2. 目標與觀察者間建立了一套觸發機制。
缺點
1. 目標與觀察者間的依賴並無完全切除。
2. 當觀察者物件多時,會影響效能。
Observer Pattern的使用時機
1. 物件之間存在一對多的關係,一個物件狀態改變會影響其他物件時。
  • 範例程式碼


範例:Observer Pattern 實作

  • References


  • 觀察者模式 (Observer Pattern)
  • 觀察者模式