天天看點

「設計模式專題」觀察者模式實戰詳細分析

作者:網際網路前沿技術研究院

一 什麼是觀察者模式

定義對象間一種一對多的依賴關系,使得每當一個對象改變狀态,則所有依賴于它的對象都會得到通知,并自動更新。

新手經常會把觀察者模式經常與釋出訂閱模式,其實二者還是有一些差別的,

二 觀察者模式與釋出訂閱模式的差別

  • 觀察者模式主要的使用場景是一對多的狀态,且需要知道通知的對象是誰。
  • 釋出訂閱模式的主要使用場景是一對多或者多對多的狀态,且不需要知道通知的對象也就是消費者是誰。
  • 觀察者模式屬于Gang of Four 提出的23中設計模式中的一種,釋出訂閱模式并不屬于其中的一種而是一種軟體設計理念。
  • 觀察者模式耦合度較高,因為觀察者模式中目标的隻有一個,觀察者需要知道目标的所有行為。
  • 釋出訂閱的耦合度較低,而釋出訂閱的釋出者可以有多個,訂閱者不需要知道釋出者是誰,隻需要關心釋出的内容。

三 編寫demo案例

比如今天是周一,Leader需要公司裡的員工加班,員工分别有Jack、Mark,然而每個人對加不加班是有自己的看法和行為的,這時就可以把Leader作為目标/主題,Jack、Mark做為觀察者,觀察者需要根據目标的指令來做出對應的行為。

一般觀察者模式有4個角色,分别為:抽象目标類、具體目标類、抽象觀察類、具體觀察類。

代碼實作如下:

抽象目标類 :MySubject

import java.util.List;

/**
 * @author liuy 抽象目标類
 * @create 2022-08-18-13:05
 */
public abstract class MySubject {
    protected List<MyObserver> observers = new ArrayList<>();

    //注冊方法
    public void attach(MyObserver observer){
        observers.add(observer);
    }

    //登出方法
    public void detach(MyObserver observer){
        observers.remove(observer);
    }

    public abstract void push(int state); //抽象通知方法

}           

具體目标類:Leader

/**
 * @author liuy 具體目标類
 * @create 2022-08-18-13:05
 */
public class Leader extends MySubject {
    @Override
    public void push(int state) {
       observers.forEach(obs->obs.response(state));
    }
}           

抽象觀察者類:MyObserver

/**
 * @author liuy 抽象觀察者類
 * @create 2022-08-18-13:05
 */
public interface MyObserver {
    void response(int state); //抽象響應方法
}
           

具體觀察者類一:Jack

/**
 * @author liuy 具體觀察者類
 * @create 2022-08-18-13:05
 */
public class Jack implements MyObserver {
    @Override
    public void response(int state) {
        String name = "Jack:";
        if(state ==1){
            System.out.println(name + "需要加班,開擺!");
        }else {
            System.out.println(name + "哈哈哈,不加班就可以回家打遊戲了");
        }
    }
}           

具體觀察者類二:Mark

/**
 * @author liuy 具體觀察者類
 * @create 2022-08-18-13:05
 */
public class Mark implements MyObserver {
    @Override
    public void response(int state) {
        String name = "Mark:";
        if(state ==1){
        System.out.println(name + "我愛加班,因為有加班費");
        }else {
            System.out.println(name + "嗚嗚嗚,沒加班費拿了");
        }
    }
}           

現在基本的觀察者模式已經實作了,現在開始測試。

/**
 * @author liuy 用戶端測試
 * @create 2022-08-18-13:05
 */
public class Client {
    public static void main(String[] args) {
        MySubject subject = new Leader();

        MyObserver obs1, obs2;
        obs1 = new Jack();
        obs2 = new Mark();
        subject.attach(obs1);
        subject.attach(obs2);


//        subject.detach(ObserverEnum.Dog.getMyObserver());

        System.out.println("===================== 第一天 =======================");

        //1代表需要加班,2代表不加班
        System.out.println("Leader:今天所有人都要加班!");
        subject.push(1);

        System.out.println("===================== 第二天 ====================");

        //1代表需要加班,2代表不加班
        System.out.println("Leader:今天大家不用加班了!");
        subject.push(2);
    }
}           

測試結果

「設計模式專題」觀察者模式實戰詳細分析

相信有一些小夥伴已經發現了,如果像測試類中的寫法,每次發送消息的時候都需要new對應的對象然後添加到觀察者集合中,這樣很不友善

我們可以使用觀察者模式+工廠模式來解決這個問題。

四 優化demo案例 觀察者模式+工廠模式

建立觀察者工廠枚舉類:ObserverEnum

/**
 * @Author liuy
 * @Description 觀察者工廠枚舉類
 * @Date 2022/8/18 14:55
 * @Version 1.0
 */
public enum ObserverEnum {
    Mouse(new Jack()),
    Dog(new Mark()),
    ;

    ObserverEnum(MyObserver myObserver) {
        this.myObserver = myObserver;
    }

    private  MyObserver  myObserver;
    public MyObserver getMyObserver() {
        return myObserver;
    }

    public void setMyObserver(MyObserver myObserver) {
        this.myObserver = myObserver;
    }

    public static List<MyObserver> getObservers(){
        return Arrays.stream(ObserverEnum.values()).map(ObserverEnum::getMyObserver).collect(Collectors.toList());
    }
}           

然後隻需要修改抽象目标類中的觀察者集合成員變量:observers

import java.util.List;

/**
 * @author liuy 抽象目标類
 * @create 2022-08-18-13:05
 */
public abstract class MySubject {
    //所有觀察者集合
    //舊 protected List<MyObserver> observers = new ArrayList<>() ;
    protected List<MyObserver> observers = ObserverEnum.getObservers();

    //注冊方法
    public void attach(MyObserver observer){
        observers.add(observer);
    }

    //登出方法
    public void detach(MyObserver observer){
        observers.remove(observer);
    }

    public abstract void push(int state); //抽象通知方法

}           

測試

/**
 * @author liuy 用戶端測試
 * @create 2022-08-18-13:05
 */
public class Client {
    public static void main(String[] args) {
        MySubject subject = new Leader();

        System.out.println("===================== 第一天 =======================");

        //1代表需要加班,2代表不加班
        System.out.println("Leader:今天所有人都要加班!");
        subject.push(1);

        System.out.println("===================== 第二天 ====================");

        //1代表需要加班,2代表不加班
        System.out.println("Leader:今天大家不用加班了!");
        subject.push(2);
    }
}           
「設計模式專題」觀察者模式實戰詳細分析

五 優缺點

主要優點

(1)觀察者模式可以實作表示層和資料邏輯層的分離,定義了穩定的消息傳遞機制,并抽象了更新接口,使得可以有各種各樣的表示層充當具體的觀察者角色。

(2)觀察者模式在觀察目标和觀察者之間建立一個抽象的耦合。觀察者對象隻需要維持一個抽象觀察者的集合,無需了解其具體觀察者。

(3)觀察者模式支援廣播通信,觀察目标會向所有已注冊的觀察者發送通知,降低了一對多系統的設計難度。

(4)觀察者模式滿足開閉原則的要求,增加新的具體觀察者無須修改原有的系統代碼。

主要缺點

(1)如果一個觀察目标對象有很多的直接觀察者和間接觀察者,那麼所有的觀察者接收到消息會耗費大量的時間。

(2)如果觀察者和被觀察者之間存在循環依賴,那麼觀察目标會觸發它們之間進行循環調用,可能導緻系統崩潰。

(3)觀察者模式沒有相應的機制讓觀察者知道所觀察的目标對象是怎麼發生變化的,而僅僅隻是知道目标觀察對象發生了變化

繼續閱讀