回調(diào)與觀察者模式

觀察者模式

觀察者模式是為了滿足監(jiān)聽(tīng)的需求。也就是說(shuō)當(dāng)某件事情發(fā)生的時(shí)候, 一個(gè)或多個(gè)觀察者需要獲知此事件的發(fā)生, 如果每個(gè)觀察者都采用輪詢的方式判斷事件是否發(fā)生,則會(huì)耗費(fèi)較多的資源。所以這個(gè)任務(wù)就應(yīng)該由被觀察者來(lái)完成, 即被觀察者持有多個(gè)觀察者對(duì)象, 當(dāng)自身某事件發(fā)生的時(shí)候, 去通知所有觀察者。這樣一種機(jī)制就是觀察者模式。

但是這其中會(huì)有一些安全問(wèn)題,比如說(shuō)被觀察者持有觀察者對(duì)象,這時(shí)觀察者就完全暴露給了被觀察者,這種情況應(yīng)該避免出現(xiàn)。所以就自然引出了接口的概念,以一個(gè)接口來(lái)統(tǒng)一觀察者的行為, 被觀察者只持有該接口, 任何一個(gè)實(shí)現(xiàn)了該接口的對(duì)象,都可以作為觀察者被其持有, 而對(duì)象的其他細(xì)節(jié)則不對(duì)被觀察者開(kāi)放。
Java中已經(jīng)封裝了觀察者模式,我們可以仿照它的寫(xiě)法來(lái)實(shí)現(xiàn)自己的觀察者模式
首先, 由我們之前提到的, 觀察者應(yīng)該提供統(tǒng)一接口用來(lái)讓被觀察者調(diào)用,即:
Observer

public interface Observer {
    void update(Observable observable, Object object);
}

對(duì)于觀察者,則比較復(fù)雜, 我們可以找出幾個(gè)主要的步驟:

序號(hào) 步驟
1 持有觀察者
2 判斷是否有改變
3 通知觀察者

這樣我們就可以寫(xiě)出被觀察者的抽象類:

public abstract class Observable {

    private Vector<Observer>observers = new Vector<>();
    private boolean isChanged;
    
    public void addObserver(Observer observer){
        observers.add(observer);
    }
    
    public void setChanged(){
        isChanged = true;
    }
    public void notifyObservers(Object object){
        if (isChanged) {
            observers.forEach(observer -> observer.update(this, object));
            isChanged = false;
        }
    }
}

這樣我們就定義了觀察者的接口,以及被觀察者的抽象類

其實(shí),在jdk的實(shí)現(xiàn)中,Observable 會(huì)有完善的線程安全保護(hù), 比如存放觀察者的List是以Vector來(lái)實(shí)現(xiàn)的,而Vector本身就是線程安全的, 再比如Observable 中的方法都加上了synchronize標(biāo)識(shí)符, 以線程安全的方式來(lái)通知觀察者。但是這些在這里都不是重點(diǎn),所以就不再詳述</font>

下面看一個(gè)實(shí)際應(yīng)用:

在我們的例子中,有一名學(xué)生和若干老師, 學(xué)生會(huì)提出一些問(wèn)題,這些問(wèn)題則會(huì)提交給老師(通知觀察者),老師則判斷自己是否會(huì)做,給學(xué)生反饋。>在這里我們有兩位老師,一位是數(shù)學(xué)老師, 一位是美術(shù)老師, 目前的設(shè)定是他們每人只回答一個(gè)問(wèn)題, 遇到其余的問(wèn)題則會(huì)跳過(guò) 。

看一下具體代碼:

Tea_Math

public class Tea_Math implements Observer{

    private String name = "數(shù)學(xué)老師:";
    @Override
    public void update(Observable observable, Object object) {
        String question = (String) object;
        if(question.equals("矩陣相乘的意義是什么呢?")){
            System.out.println(name +"從某種角度來(lái)說(shuō), 是坐標(biāo)的變換");
        }else {
            System.out.println(name +"我不太清楚, 你問(wèn)問(wèn)其他老師");
        }
        
    }
    
}

Tea_Art

public class Tea_Art implements Observer{

    private String name = "美術(shù)老師:";
    @Override
    public void update(Observable observable, Object object) {
        String question = (String) object;
        if(question.equals("莫奈的睡蓮是他晚年的作品嗎?")){
            System.out.println(name +"是他晚年一系列的作品");
        }else {
            System.out.println(name +"我不太清楚, 你問(wèn)問(wèn)其他老師");
        }
        
    }
    
}

Stu

public class Stu extends Observable{
    
    public void ask(String question){
        System.out.println("question:" + question);
        setChanged();
        notifyObservers(question);
    }
}

實(shí)際調(diào)用:

public class Client {
    
    public static void main(String[] args) {
        
        Stu stu = new Stu();
        
        Tea_Math tea_Math = new Tea_Math();
        Tea_Art tea_Art = new Tea_Art();
        
        stu.addObserver(tea_Math);
        stu.addObserver(tea_Art);
        
        stu.ask("矩陣相乘的意義是什么呢?");
        
        stu.ask("莫奈的睡蓮是他晚年的作品嗎?");
    }
    
}

我們可以看一下運(yùn)行結(jié)果:

question:矩陣相乘的意義是什么呢?

數(shù)學(xué)老師:從某種角度來(lái)說(shuō), 是坐標(biāo)的變換
美術(shù)老師:我不太清楚, 你問(wèn)問(wèn)其他老師

question:莫奈的睡蓮是他晚年的作品嗎?

數(shù)學(xué)老師:我不太清楚, 你問(wèn)問(wèn)其他老師
美術(shù)老師:是他晚年一系列的作品

以上就是觀察者模式的說(shuō)明以及代碼實(shí)現(xiàn),應(yīng)該是比較清晰的。

回調(diào):

回調(diào)是則是為了滿足調(diào)用者的需求而設(shè)計(jì)的。如調(diào)用者需要執(zhí)行某個(gè)動(dòng)作, 并且它要自己定義完成該動(dòng)作后該做什么工作。這個(gè)時(shí)候該動(dòng)作是調(diào)用者自己發(fā)出的, 但是這個(gè)動(dòng)作的完成需要交由被調(diào)用者來(lái)實(shí)現(xiàn), 這樣的話, 調(diào)用者該如何知道完成該動(dòng)作后接下來(lái)做什么呢?它怎么才能知道調(diào)用者定義的后續(xù)工作呢?這時(shí)候就需要用到回調(diào), 要實(shí)現(xiàn)一個(gè)回調(diào)的基本步驟有:

序號(hào) 步驟
1 調(diào)用者定義某個(gè)動(dòng)作
2 然后指定執(zhí)行該動(dòng)作的被調(diào)用者(持有被調(diào)用者)
3 再定義動(dòng)作完成后需要執(zhí)行的后續(xù)動(dòng)作
4 最后將這個(gè)后續(xù)動(dòng)作告知被調(diào)用者(或者可以說(shuō)將這個(gè)后續(xù)動(dòng)作的調(diào)用方法交給被調(diào)用者)。通常情況下, 被調(diào)用者是系統(tǒng)應(yīng)用, 也就是說(shuō), 我們將自己的后續(xù)動(dòng)作告知系統(tǒng)應(yīng)用, 讓其完成后執(zhí)行我們的操作。

可以用點(diǎn)擊事件的實(shí)現(xiàn)來(lái)深入一下:

如有一個(gè)TextView作為調(diào)用者, 一個(gè)OnClickListener作為被調(diào)用者TextView應(yīng)該持有OnClickListener, 所以它有一個(gè)setOnClickListener方法而且它要執(zhí)行onClick動(dòng)作,所以它有click方法。而對(duì)于被調(diào)用者OnClickListener來(lái)說(shuō), 它是click動(dòng)作的實(shí)際完成者,所以它有onClick方法。
這樣的話就可以寫(xiě)出二者的結(jié)構(gòu):

TextView

public class TextView {
    private OnClickListener onClickListener;
    public void setOnClickListener(OnClickListener onClickListener){
        this.onClickListener = onClickListener;
    }
    public void click(){
        onClickListener.onClick("textview", 1);
    }
}

OnClickListener

public interface OnClickListener {
    void onClick(String view, int position);
}

實(shí)際調(diào)用:

public class Client_Click {

    public static void main(String[] args) {
        TextView textView = new TextView();
        textView.setOnClickListener(new OnClickListener() {
            
            @Override
            public void onClick(String view, int position) {
                System.out.println(view);
                System.out.println(position);
            }
        });
        textView.click();
    }
}

上面的代碼已經(jīng)比較清晰了, 應(yīng)該可以理解回調(diào)的意義了

對(duì)于回調(diào)和觀察者模式的聯(lián)系和區(qū)別, 以及他們的適用環(huán)境,可以結(jié)合實(shí)際情況來(lái)判斷。我打算過(guò)些日子有時(shí)間詳細(xì)的寫(xiě)一下。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1 場(chǎng)景問(wèn)題# 1.1 訂閱報(bào)紙的過(guò)程## 來(lái)考慮實(shí)際生活中訂閱報(bào)紙的過(guò)程,這里簡(jiǎn)單總結(jié)了一下,訂閱報(bào)紙的基本流程...
    七寸知架構(gòu)閱讀 4,814評(píng)論 5 57
  • 了解 RxJava 的應(yīng)該都知道是一個(gè)基于事務(wù)驅(qū)動(dòng)的庫(kù),響應(yīng)式編程的典范。提到事務(wù)驅(qū)動(dòng)和響應(yīng)就不得不說(shuō)說(shuō),設(shè)計(jì)模式...
    騎摩托馬斯閱讀 694評(píng)論 0 1
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 34,840評(píng)論 18 399
  • 項(xiàng)目代碼因?yàn)闃I(yè)務(wù)需求變跟最近在大改,其中使用了大量的Handler機(jī)制,作為新時(shí)代的程序猿,我不想使用這個(gè)以及...
    騎小豬看流星閱讀 1,164評(píng)論 2 50
  • 上一篇中我們了解了什么是RxJava,用一個(gè)詞來(lái)總結(jié)就是異步。這里我們來(lái)講講RxJava的異步實(shí)現(xiàn)。它是通過(guò)一種擴(kuò)...
    于闐閱讀 2,247評(píng)論 0 2

友情鏈接更多精彩內(nèi)容