溫馨提示×

溫馨提示×

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

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

怎么理解Java設(shè)計(jì)模式的觀察者模式

發(fā)布時(shí)間:2021-11-08 09:02:16 來源:億速云 閱讀:136 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“怎么理解Java設(shè)計(jì)模式的觀察者模式”的有關(guān)知識,在實(shí)際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

    一、什么是觀察者模式

    在許多設(shè)計(jì)中,經(jīng)常涉及多個(gè)對象都對一個(gè)特殊對象中的數(shù)據(jù)變化感興趣,而且這多個(gè)對象都希望跟蹤那個(gè)特殊對象中的數(shù)據(jù)變化,也就是說當(dāng)對象間存在一對多關(guān)系時(shí),在這樣的情況下就可以使用觀察者模式。當(dāng)一個(gè)對象被修改時(shí),則會自動通知它的依賴對象。

    觀察者模式是關(guān)于多個(gè)對象想知道一個(gè)對象中數(shù)據(jù)變化情況的一種成熟的模式。觀察者模式中有一個(gè)稱作“主題”的對象和若干個(gè)稱作“觀察者”的對象,“主題”和“觀察者”間是一種一對多的依賴關(guān)系,當(dāng)“主題”的狀態(tài)發(fā)生變化時(shí),所有“觀察者”都得到通知。

    主要解決:一個(gè)對象狀態(tài)改變給其他對象通知的問題,而且要考慮到易用和低耦合,保證高度的協(xié)作。

    二、觀察者模式的結(jié)構(gòu)

    怎么理解Java設(shè)計(jì)模式的觀察者模式

    觀察者模式的結(jié)構(gòu)中包含四種角色:

    (1)主題(Subject):主題是一個(gè)接口,該接口規(guī)定了具體主題需要實(shí)現(xiàn)的方法,比如,添加、刪除觀察者以及通知觀察者更新數(shù)據(jù)的方法。

    (2)觀察者(Observer):觀察者是一個(gè)接口,該接口規(guī)定了具體觀察者用來更新數(shù)據(jù)的方法。

    (3)具體主題(ConcreteSubject):具體主題是實(shí)現(xiàn)主題接口類的一個(gè)實(shí)例,該實(shí)例包含有可以經(jīng)常發(fā)生變化的數(shù)據(jù)。具體主題需使用一個(gè)集合,比如ArrayList,存放觀察者的引用,以便數(shù)據(jù)變化時(shí)通知具體觀察者。

    (4)具體觀察者(ConcreteObserver):具體觀察者是實(shí)現(xiàn)觀察者接口類的一個(gè)實(shí)例。具體觀察者包含有可以存放具體主題引用的主題接口變量,以便具體觀察者讓具體主題將自己的引用添加到具體主題的集合中,使自己成為它的觀察者,或讓這個(gè)具體主題將自己從具體主題的集合中刪除,使自己不再是它的觀察者。

    三、觀察者模式的使用場景

    (1)當(dāng)一個(gè)對象的數(shù)據(jù)更新時(shí)需要通知其他對象,但這個(gè)對象又不希望和被通知的那些對象形成緊耦合。

    (2)當(dāng)一個(gè)對象的數(shù)據(jù)更新時(shí),這個(gè)對象需要讓其他對象也各自更新自己的數(shù)據(jù),但這個(gè)對象不知道具體有多少對象需要更新數(shù)據(jù)。

    觀察者模式在實(shí)際項(xiàng)目的應(yīng)用中非常常見,比如你到 ATM 機(jī)器上取錢,多次輸錯(cuò)密碼,卡就會被 ATM吞掉,吞卡動作發(fā)生的時(shí)候,會觸發(fā)哪些事件呢?第一攝像頭連續(xù)快拍,第二,通知監(jiān)控系統(tǒng),吞卡發(fā)生;第三,初始化 ATM 機(jī)屏幕,返回最初狀態(tài),你不能因?yàn)榫屯塘艘粡埧?,整個(gè) ATM 都不能用了吧,一般前兩個(gè)動作都是通過觀察者模式來完成的。觀察者可以實(shí)現(xiàn)消息的廣播,一個(gè)消息可以觸發(fā)多個(gè)事件,這是觀察者模式非常重要的功能。

    使用觀察者模式也有兩個(gè)重點(diǎn)問題要解決:

    廣播鏈的問題。

    如果你做過數(shù)據(jù)庫的觸發(fā)器,你就應(yīng)該知道有一個(gè)觸發(fā)器鏈的問題,比如表 A 上寫了一個(gè)觸發(fā)器,內(nèi)容是一個(gè)字段更新后更新表 B 的一條數(shù)據(jù),而表 B 上也有個(gè)觸發(fā)器,要更新表 C,表 C 也有觸發(fā)器…,完蛋了,這個(gè)數(shù)據(jù)庫基本上就毀掉了!我們的觀察者模式也是一樣的問題,一個(gè)觀察者可以有雙重身份,即使觀察者,也是被觀察者,這沒什么問題呀,但是鏈一旦建立,這個(gè)邏輯就比較復(fù)雜,可維護(hù)性非常差,根據(jù)經(jīng)驗(yàn)建議,在一個(gè)觀察者模式中最多出現(xiàn)一個(gè)對象既是觀察者也是被觀察者,也就是說消息最多轉(zhuǎn)發(fā)一次(傳遞兩次),這還是比較好控制的;

    異步處理問題。

    被觀察者發(fā)生動作了,觀察者要做出回應(yīng),如果觀察者比較多,而且處理時(shí)間比較長怎么辦?那就用異步唄,異步處理就要考慮線程安全和隊(duì)列的問題,這個(gè)大家有時(shí)間看看 Message Queue,就會有更深的了解。

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

    優(yōu)點(diǎn):

    1、具體主題和具體觀察者是松耦合關(guān)系。由于主題接口僅僅依賴于觀察者接口,因此具體主題只是知道它的觀察者是實(shí)現(xiàn)觀察者接口的某個(gè)類的實(shí)例,但不需要知道具體是哪個(gè)類。同樣,由于觀察者僅僅依賴于主題接口,因此具體觀察者只是知道它依賴的主題是實(shí)現(xiàn)主題接口的某個(gè)類的實(shí)例,但不需要知道具體是哪個(gè)類。

    2、觀察者模式滿足“開-閉原則”。主題接口僅僅依賴于觀察者接口,這樣,就可以讓創(chuàng)建具體主題的類也僅僅是依賴于觀察者接口,因此,如果增加新的實(shí)現(xiàn)觀察者接口的類,不必修改創(chuàng)建具體主題的類的代碼。。同樣,創(chuàng)建具體觀察者的類僅僅依賴于主題接口,如果增加新的實(shí)現(xiàn)主題接口的類,也不必修改創(chuàng)建具體觀察者類的代碼。

    缺點(diǎn):

    1、如果一個(gè)被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費(fèi)很多時(shí)間。

    2、如果在觀察者和觀察目標(biāo)之間有循環(huán)依賴的話,觀察目標(biāo)會觸發(fā)它們之間進(jìn)行循環(huán)調(diào)用,可能導(dǎo)致系統(tǒng)崩潰。

    3、觀察者模式?jīng)]有相應(yīng)的機(jī)制讓觀察者知道所觀察的目標(biāo)對象是怎么發(fā)生變化的,而僅僅只是知道觀察目標(biāo)發(fā)生了變化。

    五、觀察者模式的實(shí)現(xiàn)

    Observer類---抽象觀察者,為所有具體觀察者定義一個(gè)接口,在得到主題通知時(shí)更新自己。

    這個(gè)接口叫做更新接口,抽象觀察者一般用一個(gè)抽象類或者一個(gè)接口實(shí)現(xiàn)。更新接口通常包括一個(gè)Update方法,這個(gè)方法叫做更新方法。

    abstract class Observer
    {
        public abstract void Update();
    }

    Subject類---主題或者抽象通知者,一般用一個(gè)抽象類或者一個(gè)接口實(shí)現(xiàn)。

    它把所有對觀察者對象的引用保存到一個(gè)聚集里,每個(gè)主題都可以有任何數(shù)量的觀察者。抽象主題提供一個(gè)接口,可以增加和刪除觀察者。

    abstract class Subject
    {
        private List<Observer> observers = new List<Observer>();
        //增加觀察者
        public void Attach(Observer observer)
        {
            observers.Add(observer);
        }
        //移除觀察者
        public void Detach(Observer observer)
        {
            observers.Remove(observer);
        }
        //通知
        public void Notify()
        {
            foreach (var item in observers)
            {
                item.Update();
            }
        }
    }

    ConcreteSubject類---具體主題或者具體通知者,將有關(guān)狀態(tài)存入具體觀察者對象;在具體主題的內(nèi)部狀態(tài)改變時(shí),給所有登記過的觀察者發(fā)送通知。

    具體主題角色通常用一個(gè)具體類實(shí)現(xiàn)。

    class ConcreteSubject : Subject
    {
        private string subjectState;
        //具體被觀察者狀態(tài)
        public string SubjectState
        {
            get { return subjectState; }
            set { subjectState = value; }
        }
    }

    ConcreteObserver類---具體觀察者,實(shí)現(xiàn)抽象觀察者角色所要求的更新接口,以便使本身的狀態(tài)與主題的狀態(tài)相協(xié)調(diào)。

    具體觀察者角色可以保存一個(gè)指向具體主題對象的引用。具體觀察者角色通常用一個(gè)具體類實(shí)現(xiàn)。

    class ConcreteObserver : Observer
    {
        private string name;
        private string observerState;
        private ConcreteSubject subject;
         public ConcreteObserver(ConcreteSubject subject, string name)
        {
            this.subject = subject;
            this.name = name;
        }
        public override void Update()
        {
            observerState = subject.SubjectState;
            Console.WriteLine("觀察者{0}的新狀態(tài)是{1}", name, observerState);
        }
        public ConcreteSubject Subject
        {
            get { return subject; }
            set { subject = value; }
        }
    }

    客戶端代碼

    static void Main(string[] args)
    {
        ConcreteSubject cs = new ConcreteSubject();
         cs.Attach(new ConcreteObserver(cs, "X"));
        cs.Attach(new ConcreteObserver(cs, "Y"));
        cs.Attach(new ConcreteObserver(cs, "Z"));
         cs.SubjectState = "ABC";
        cs.Notify();
         Console.Read();
    }

    結(jié)果

    觀察者X的新狀態(tài)是ABC
    觀察者Y的新狀態(tài)是ABC
    觀察者Z的新狀態(tài)是ABC

    六、觀察者模式和委托的結(jié)合

    上述代碼盡管已經(jīng)用了依賴倒轉(zhuǎn)原則,但是“抽象通知者”還是依賴“抽象觀察者”,也就是說,萬一沒有了抽象觀察者這樣的接口,這個(gè)通知功能就發(fā)送不了。

    另外就是每個(gè)具體觀察者,它不一定是Update的方法調(diào)用。

    目的:通知者和觀察者之間根本就互相不知道,由客戶端來決定通知誰

    //通知者接口
    interface Subject
    {
        void Notify();
        string SubjectState { get; set; }
    }

    具體觀察者類

    //看股票的同事
    class StockObserver
    {
        private string name;
        private Subject sub;
         public StockObserver(string name, Subject sub)
        {
            this.sub = sub;
            this.name = name;
        }
        //關(guān)閉股票
        public void CloseStock()
        {
            Console.WriteLine("{0}{1}關(guān)閉股票,繼續(xù)工作", sub.SubjectState, sub);
        }
    }
    //看NBA的同事
    class NBAObserver
    {
        private string name;
        private Subject sub;
         public NBAObserver(string name, Subject sub)
        {
            this.sub = sub;
            this.name = name;
        }
        //關(guān)閉NBA
        public void CloseNBA()
        {
            Console.WriteLine("{0}{1}關(guān)閉NBA,繼續(xù)工作", sub.SubjectState, sub);
        }
    }

    聲明一個(gè)委托,無參數(shù),無返回值

    //聲明一個(gè)委托,無參數(shù),無返回值
    delegate void EventHandler();

    主題或者抽象通知者

    //老板類
    class Boss : Subject
    {
        private string action;
        //聲明委托事件Update
        public event EventHandler Update;
         public string SubjectState
        {
            get { return action; }
             set { action = value; }
        }
         public void Notify()
        {
            //在訪問通知時(shí),調(diào)用Update
            Update();
        }
    }
    //秘書類
    class Secretary : Subject
    {
        //與老板類類似,省略......
    }

    客戶端代碼

    static void Main(string[] args)
    {
        //老板張
        Boss Zhang = new Boss();
         StockObserver tongshi1 = new StockObserver("張三",Zhang);
        NBAObserver tongshi2 = new NBAObserver("李四",Zhang);
         Zhang.Update += new EventHandler(tongshi1.CloseStock);
        Zhang.Update += new EventHandler(tongshi2.CloseNBA);
         Zhang.SubjectState = "老板張駕到!";
        Zhang.Notify();
        Console.Read();
    }

    結(jié)果

    老板張駕到!張三關(guān)閉股票,繼續(xù)工作
    老板張駕到!李四關(guān)閉NBA,繼續(xù)工作

    “怎么理解Java設(shè)計(jì)模式的觀察者模式”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

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

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

    AI