溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

折騰Java設(shè)計(jì)模式之觀察者模式

發(fā)布時(shí)間:2020-08-01 22:00:13 來源:網(wǎng)絡(luò) 閱讀:208 作者:大萌小路 欄目:軟件技術(shù)

觀察者模式

Define a one-to-many dependency between objects where a state change in one object results in all its dependents being notified and updated automatically.

直譯過來就是,定義對(duì)象間的一對(duì)多依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)變更會(huì)自動(dòng)通知和更新所有依賴項(xiàng)。像發(fā)布/訂閱模式,事件通知模式,數(shù)據(jù)源/監(jiān)聽模式等都是性質(zhì)一樣。

觀察者模式UML

UML類和時(shí)序圖

類圖:Subject類不會(huì)直接更新從屬對(duì)象的狀態(tài)。相反,Subject引用了用于更新狀態(tài)的觀察者接口(update()),這使得Subject獨(dú)立于依賴對(duì)象的狀態(tài)更新方式。Observer1和Observer2類通過將狀態(tài)與Subject的狀態(tài)同步來實(shí)現(xiàn)Observer接口。

時(shí)序圖:Observer1和Observer2對(duì)象調(diào)用Subject1上的attach(this)來注冊(cè)自己。假如Subject1的狀態(tài)發(fā)生變更,Subject1本身調(diào)用notify()。notify()對(duì)已注冊(cè)的Observer1和Observer2對(duì)象調(diào)用update(),后者從Subject1請(qǐng)求已更改的數(shù)據(jù)(getState())以更新(同步)其狀態(tài)。

折騰Java設(shè)計(jì)模式之觀察者模式cdn.xitu.io/2019/4/8/169fa85625382e27?w=600&h=240&f=jpeg&s=26680">

UML類圖

折騰Java設(shè)計(jì)模式之觀察者模式

觀察者模式角色

Subject(目標(biāo)):目標(biāo)又稱為主題,它是指被觀察的對(duì)象。在目標(biāo)中定義了一個(gè)觀察者集合,一個(gè)觀察目標(biāo)可以接受任意數(shù)量的觀察者來觀察,它提供一系列方法來增加和刪除觀察者對(duì)象,同時(shí)它定義了通知方法notify()。目標(biāo)類可以是接口,也可以是抽象類或具體類。

ConcreteSubject(具體目標(biāo)):具體目標(biāo)是目標(biāo)類的子類,通常它包含有經(jīng)常發(fā)生改變的數(shù)據(jù),當(dāng)它的狀態(tài)發(fā)生改變時(shí),向它的各個(gè)觀察者發(fā)出通知;同時(shí)它還實(shí)現(xiàn)了在目標(biāo)類中定義的抽象業(yè)務(wù)邏輯方法(如果有的話)。如果無須擴(kuò)展目標(biāo)類,則具體目標(biāo)類可以省略。

Observer(觀察者):觀察者將對(duì)觀察目標(biāo)的改變做出反應(yīng),觀察者一般定義為接口,該接口聲明了更新數(shù)據(jù)的方法update(),因此又稱為抽象觀察者。

ConcreteObserver(具體觀察者):在具體觀察者中維護(hù)一個(gè)指向具體目標(biāo)對(duì)象的引用,它存儲(chǔ)具體觀察者的有關(guān)狀態(tài),這些狀態(tài)需要和具體目標(biāo)的狀態(tài)保持一致;它實(shí)現(xiàn)了在抽象觀察者Observer中定義的update()方法。

觀察者模式描述了如何建立對(duì)象與對(duì)象之間的依賴關(guān)系,以及如何構(gòu)造滿足這種需求的系統(tǒng)。觀察者模式包含觀察目標(biāo)和觀察者兩類對(duì)象,一個(gè)目標(biāo)可以有任意數(shù)目的與之相依賴的觀察者,一旦觀察目標(biāo)的狀態(tài)發(fā)生改變,所有的觀察者都將得到通知。作為對(duì)這個(gè)通知的響應(yīng),每個(gè)觀察者都將監(jiān)視觀察目標(biāo)的狀態(tài)以使其狀態(tài)與目標(biāo)狀態(tài)同步,這種交互也稱為發(fā)布-訂閱(Publish-Subscribe)。觀察目標(biāo)是通知的發(fā)布者,它發(fā)出通知時(shí)并不需要知道誰是它的觀察者,可以有任意數(shù)目的觀察者訂閱它并接收通知。

干貨源碼解析

源碼地址

博客訂閱的功能,抽象主題中維護(hù)訂閱關(guān)系,同時(shí)引入普通和vip觀察者。

//抽象主題
@Data
public abstract class Subject {

    //主題訂閱者們
    private List<Observer> observerList = Lists.newArrayList();

    //訂閱
    public void register(Observer observer) {
        observerList.add(observer);
    }

    //取消訂閱
    public void remove(Observer observer) {
        observerList.remove(observer);
    }

    //發(fā)布東西
    public abstract void publish(String msg);
}

//抽象觀察者
@Slf4j
@Data
@AllArgsConstructor
public abstract class Observer {

    //觀察者名稱
    private String name;

    //更新狀態(tài),由主題調(diào)度
    public void update(Object subject, Object args) {
        log.info("{}獲取到變更通知:{}", name, args);
    }
}

//博客主題
@Slf4j
public class Blog extends Subject {

    @Override
    public void publish(String msg) {
        log.info("發(fā)布msg:{}", msg);
        //通知訂閱者
        getObserverList().forEach(observer -> observer.update(this, msg));
    }
}

//普通用戶觀察者
@Slf4j
public class NormalObserver extends Observer {

    public NormalObserver(String name) {
        super(name);
    }

    @Override
    public void update(Object subject, Object args) {
        super.update(subject, args);
        log.info("{}獲取到變更通知:普通用戶可以不緩存", getName());
    }
}

//vip用戶觀察者
@Slf4j
public class VipObserver extends Observer {

    public VipObserver(String name) {
        super(name);
    }

    @Override
    public void update(Object subject, Object args) {
        super.update(subject, args);
        log.info("{}獲取到變更通知:vip可以緩存", getName());
    }
}

@Slf4j
public class Application {

    public static void main(String[] args) {
        Blog blog = new Blog();
        VipObserver wang = new VipObserver("老王99");
        VipObserver lee = new VipObserver("老李");
        NormalObserver four = new NormalObserver("小四");
        NormalObserver twoEgg = new NormalObserver("二蛋");
        log.info("---------------------begin--------------------");

        // 用戶訂閱博客,普通和vip用戶
        new Thread(() -> {
            blog.register(wang);
            sleep(2);
            blog.register(lee);
            sleep(2);
            blog.register(four);
        }).start();

        // 博客線程每隔2秒發(fā)布一次文章,  總共發(fā)布4次
        new Thread(() -> {
            IntStream.rangeClosed(1, 4).forEach(i -> {
                blog.publish(String.format("新把戲第%s次", i));
                sleep(2);
            });
        }).start();

        // 有用戶退出訂閱博客,也有二蛋加入訂閱
        new Thread(() -> {
            sleep(3);
            blog.remove(lee);
            sleep(2);
            blog.register(twoEgg);
        }).start();
    }

    private static void sleep(int seconds) {
        try {
            TimeUnit.SECONDS.sleep(seconds);
        } catch (InterruptedException e) {
            log.error("error : ", e);
        }
    }
}

折騰Java設(shè)計(jì)模式之觀察者模式

jdk內(nèi)置的Obverser和Observable

@Slf4j
public class ObserverApplication {

    public static void main(String[] args) {
        log.info("Enter Text: ");
        EventSource eventSource = new EventSource();

        eventSource.addObserver((obj, arg) -> {
            log.info("Received response: {}", arg);
        });

        eventSource.addObserver((obj, arg) -> {
            log.info("Received response2: {},,{}", arg, obj
            );
        });

        new Thread(eventSource).start();
    }
}

class EventSource extends Observable implements Runnable {
    public void run() {
        while (true) {
            String response = new Scanner(System.in).next();
            setChanged();
            notifyObservers(response);
        }
    }
}

Java中的使用

它使得一個(gè)對(duì)象可以靈活的將消息發(fā)送給感興趣的對(duì)象

java.util.EventListener

javax.servlet.http.HttpSessionBindingListener

javax.servlet.http.HttpSessionAttributeListener

javax.faces.event.PhaseListener

Listener從名字上看就明白是監(jiān)聽的意思了。

JDK中內(nèi)置的Obverser和Observable

jdk中內(nèi)置的觀察者模式。即是java.util.Observer(接口)和java.util.Observable(類)。

簡(jiǎn)單的說說這個(gè)。觀察者接口(java.util.Observer),主題(java.util.Observable)。實(shí)現(xiàn)觀察者接口和繼承主題。通過抽象主題的addObserver()注冊(cè)觀察者,deleteObserver()移除觀察者。

首先通過調(diào)用主題類的setChange()告知狀態(tài)變更,隨后調(diào)用notifyObservers方法(可傳可不傳參數(shù))去通知觀察者,最后因?yàn)橹黝}在notifyObservers時(shí)會(huì)主動(dòng)調(diào)用觀察者的update()方法,改方法有2個(gè)參數(shù),第一個(gè)為主題對(duì)象,第二個(gè)為可變參數(shù)。

總結(jié)

觀察者模式是一種使用頻率非常高的設(shè)計(jì)模式,JDK中就帶有實(shí)現(xiàn)。無論是移動(dòng)應(yīng)用、Web應(yīng)用或者桌面應(yīng)用,觀察者模式幾乎無處不在,它為實(shí)現(xiàn)對(duì)象之間的聯(lián)動(dòng)提供了一套完整的解決方案,凡是涉及到一對(duì)一或者一對(duì)多的對(duì)象交互場(chǎng)景都可以使用觀察者模式。觀察者模式廣泛應(yīng)用于各種編程語言的GUI事件處理的實(shí)現(xiàn),在基于事件的XML解析技術(shù)(如SAX2)以及Web事件處理中也都使用了觀察者模式。

觀察者模式的優(yōu)點(diǎn)

(1) 觀察者模式可以實(shí)現(xiàn)表示層和數(shù)據(jù)邏輯層的分離,定義了穩(wěn)定的消息更新傳遞機(jī)制,并抽象了更新接口,使得可以有各種各樣不同的表示層充當(dāng)具體觀察者角色。
(2) 觀察者模式在觀察目標(biāo)和觀察者之間建立一個(gè)抽象的耦合。觀察目標(biāo)只需要維持一個(gè)抽象觀察者的集合,無須了解其具體觀察者。由于觀察目標(biāo)和觀察者沒有緊密地耦合在一起,因此它們可以屬于不同的抽象化層次。
(3) 觀察者模式支持廣播通信,觀察目標(biāo)會(huì)向所有已注冊(cè)的觀察者對(duì)象發(fā)送通知,簡(jiǎn)化了一對(duì)多系統(tǒng)設(shè)計(jì)的難度。
(4) 觀察者模式滿足“開閉原則”的要求,增加新的具體觀察者無須修改原有系統(tǒng)代碼,在具體觀察者與觀察目標(biāo)之間不存在關(guān)聯(lián)關(guān)系的情況下,增加新的觀察目標(biāo)也很方便。

觀察者模式的缺點(diǎn)

(1) 如果一個(gè)觀察目標(biāo)對(duì)象有很多直接和間接觀察者,將所有的觀察者都通知到會(huì)花費(fèi)很多時(shí)間。
(2) 如果在觀察者和觀察目標(biāo)之間存在循環(huán)依賴,觀察目標(biāo)會(huì)觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用,可能導(dǎo)致系統(tǒng)崩潰。
(3) 觀察者模式?jīng)]有相應(yīng)的機(jī)制讓觀察者知道所觀察的目標(biāo)對(duì)象是怎么發(fā)生變化的,而僅僅只是知道觀察目標(biāo)發(fā)生了變化。

注意事項(xiàng)

1、JAVA 中已經(jīng)有了對(duì)觀察者模式的支持類。 2、避免循環(huán)引用。 3、如果順序執(zhí)行,某一觀察者錯(cuò)誤會(huì)導(dǎo)致系統(tǒng)卡殼,一般采用異步方式。

參考

Observer pattern

觀察者模式|菜鳥教程

細(xì)數(shù)JDK里的設(shè)計(jì)模式

設(shè)計(jì)模式總結(jié)(Java)—— 觀察者模式

歡迎關(guān)注

折騰Java設(shè)計(jì)模式之觀察者模式

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI