設(shè)計模式-觀察者模式

在我們?nèi)粘I钪?,有些人會有訂閱報紙的?jīng)歷,當我們訂閱時,都能定時的收到報紙,當我么取消訂閱時,就不會再收到報紙。
當然,互聯(lián)網(wǎng)普及之后,報紙逐漸減少。但是類似的也有許多,例如微信的公眾號,當我們訂閱了公眾號時,公眾號有新文章發(fā)布時就會推送到我們的微信上。

用戶3關(guān)注公眾號

這里寫圖片描述

公眾號把用戶3添加到關(guān)注用戶列表中,有新文章時推送給關(guān)注用戶列表中
這里寫圖片描述

用戶2取消關(guān)注,公眾號把用戶2從關(guān)注用戶列表中移除,有新文章時不再推送給用戶2
這里寫圖片描述

以上便是觀察這模式

介紹

觀察者模式定義對象間的一種一對多的依賴關(guān)系,當一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并被自動更新。
觀察者模式又叫做發(fā)布-訂閱(Publish/Subscribe)模式、模型-視圖(Model/View)模式、源-監(jiān)聽器(Source/Listener)模式或從屬者(Dependents)模式。
發(fā)生改變的對象稱為觀察目標,而被通知的對象稱為觀察者,一個觀察目標可以對應多個觀察者,而且這些觀察者之間沒有相互聯(lián)系,可以根據(jù)需要增加和刪除觀察者,使得系統(tǒng)更易于擴展,這就是觀察者模式的模式動機。

結(jié)構(gòu)圖

這里寫圖片描述

時序圖

這里寫圖片描述

案例

案例就采用模擬公眾號訂閱的機制
注:以下代碼只是提供個思路

主題抽象類(公眾號)

public abstract class Subject {
    List<UserObeserver> userList = new ArrayList<UserObeserver>();
    
    public void addUser(UserObeserver userObeserver){
        userList.add(userObeserver);
    }
    
    public void delUser(UserObeserver userObeserver){
        userList.remove(userObeserver);
    }
    
    public void pushArticle(String articleContent){
        for (UserObeserver userObeserver : userList) {
            userObeserver.receiveArticle(this,articleContent);
        }
    }
    
    public abstract String getSubjectName();
    
}

公眾號1

public class ConcreteSubject1 extends Subject{

    @Override
    public String getSubjectName() {
        return "公眾號1";
    }

}

公眾號2

public class ConcreteSubject2 extends Subject{

    @Override
    public String getSubjectName() {
        return "公眾號2";
    }

}

訂閱者接口(用戶)

public interface UserObeserver {
    void receiveArticle(Subject subject, String articleContent);
}

用戶1

public class ConcreteUserObeserver1 implements UserObeserver {

    @Override
    public void receiveArticle(Subject subject,String articleContent) {
        System.out.println("我是讀者1,公眾號名稱:"+subject.getSubjectName()+"  推出的新的文章內(nèi)容是"+articleContent);
    }

}

用戶2

public class ConcreteUserObeserver2 implements UserObeserver {

    @Override
    public void receiveArticle(Subject subject,String articleContent) {
        System.out.println("我是讀者2,公眾號名稱:"+subject.getSubjectName()+"  推出的新的文章內(nèi)容是"+articleContent);
    }

}

測試類

public class Client {
    public static void main(String[] args) {
        ConcreteSubject1 subject1 = new ConcreteSubject1();
        ConcreteSubject2 subject2 = new ConcreteSubject2();
        
        ConcreteUserObeserver1 user1 = new ConcreteUserObeserver1();
        ConcreteUserObeserver2 user2 = new ConcreteUserObeserver2();
        
        //讀者1、2都訂閱公眾號1,讀者2訂閱了公眾號2
        subject1.addUser(user1);
        subject1.addUser(user2);
        subject2.addUser(user2);
        //公眾號發(fā)布消息
        System.out.println("第一篇");
        subject1.pushArticle("1.今天天氣不錯!");
        subject2.pushArticle("1.今天天氣很不錯!");
        
        //讀者1取消關(guān)注公眾號1,想去關(guān)注公眾號2,讀者2取消關(guān)注公眾號2
        subject1.delUser(user1);
        subject2.delUser(user2);
        subject2.addUser(user1);
        
        System.out.println("第二篇");
        subject1.pushArticle("2.今天天氣一般般!");
        subject2.pushArticle("2.今天天氣很一般般!");
        
    }
}

測試結(jié)果

這里寫圖片描述

總結(jié)

這一模式中的關(guān)鍵對象是觀察目標和觀察者,一個目標可以有任意數(shù)目的與之相依賴的觀察者,一旦目標的狀態(tài)發(fā)生改變,所有的觀察者都將得到通知。
觀察者模式可以實現(xiàn)表示層和數(shù)據(jù)邏輯層的分離,并定義了穩(wěn)定的消息更新傳遞機制,抽象了更新接口,使得可以有各種各樣不同的表示層作為具體觀察者角色。
JDK中有內(nèi)置的觀察者模式,Observer接口和Observable類,有興趣的可以了解下。

延伸

觀察者模式中觀察者有沒有可能變成主題,也就是觀察者既可以保持自身的身份又可以發(fā)布新的消息?
當然可以,想現(xiàn)在的微博,你關(guān)注別人,你就能獲得那個人發(fā)布的消息,如果他也關(guān)注你,那么他也能收到你發(fā)布的消息。微信朋友圈亦是如此,當你們成為微信好友是,雙方都能收到彼此發(fā)的朋友圈。
用戶1

public class User1 extends Subject implements UserObeserver{

    @Override
    public void receiveArticle(Subject subject, String articleContent) {
        System.out.println("我是用戶1,"+subject.getSubjectName()+"  推出的新的文章內(nèi)容是"+articleContent);
    }

    @Override
    public String getSubjectName() {
        return "用戶1";
    }

}

用戶2

public class User2 extends Subject implements UserObeserver{

    @Override
    public void receiveArticle(Subject subject, String articleContent) {
        System.out.println("我是用戶2,"+subject.getSubjectName()+"  推出的新的文章內(nèi)容是"+articleContent);
    }

    @Override
    public String getSubjectName() {
        return "用戶2";
    }

}

測試類

public class Client {
    public static void main(String[] args) {
        User1 user1 = new User1();
        User2 user2 = new User2();
        
        //用戶1和用戶2互相加為好友
        user1.addUser(user2);
        user2.addUser(user1);
        
        user1.pushArticle("1.今天天氣不錯!");
        user2.pushArticle("1.今天天氣很不錯!");
        
        //用戶2把用戶1給屏蔽了(注:應該從用戶1中去掉用戶2中的觀察者)
        System.out.println("用戶2把用戶1給屏蔽了");
        user1.delUser(user2);
        user1.pushArticle("1.今天天氣不錯!");
        user2.pushArticle("1.今天天氣很不錯!");
        
    }
}
這里寫圖片描述

測試類中的用戶2把用戶1給屏蔽了,有些人可能思路轉(zhuǎn)不過來,為什么用戶2把用戶1給屏蔽了,用戶1要去刪除用戶2的對象?
換個思路就是用戶2不想在收到用戶1的消息,所以用戶1中維護的觀察者列表中要刪除這個對象。

代碼見Github地址

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

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

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