C#設(shè)計(jì)模式筆記之觀察者模式(Observer Pattern)

  1. 概述

定義對(duì)象之間的一種一對(duì)多依賴關(guān)系削咆,使得每當(dāng)一個(gè)對(duì)象狀態(tài)發(fā)生改變時(shí)号坡,其相關(guān)依賴對(duì)象皆得到通知并被自動(dòng)更新欧聘。觀察者模式的別名包括發(fā)布-訂閱(Publish/Subscribe)模式忍捡、模型-視圖(Model/View)模式形葬、源-監(jiān)聽(tīng)器(Source/Listener)模式或從屬者(Dependents)模式擦秽。觀察者模式是一種對(duì)象行為型模式码荔。
2. 模式中的角色

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

2.2 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ù)邏輯方法(如果有的話)。如果無(wú)須擴(kuò)展目標(biāo)類谅阿,則具體目標(biāo)類可以省略半哟。

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

2.4 ConcreteObserver(具體觀察者):在具體觀察者中維護(hù)一個(gè)指向具體目標(biāo)對(duì)象的引用贱田,它存儲(chǔ)具體觀察者的有關(guān)狀態(tài)缅茉,這些狀態(tài)需要和具體目標(biāo)的狀態(tài)保持一致;它實(shí)現(xiàn)了在抽象觀察者Observer中定義的update()方法男摧。通常在實(shí)現(xiàn)時(shí)蔬墩,可以調(diào)用具體目標(biāo)類的attach()方法將自己添加到目標(biāo)類的集合中或通過(guò)detach()方法將自己從目標(biāo)類的集合中刪除。

3. 模式解讀

3.1 模式的類圖

[圖片上傳失敗...(image-4ed9c7-1649316690458)]

3.2 代碼實(shí)現(xiàn)

using System;
using System.Collections.Generic;

namespace ConsoleApp2
{
    class Class16
    {
        public static void Main(string[] args)
        {
            //定義觀察目標(biāo)對(duì)象
            Team acc = new ConcreteTeam("金庸群俠");

            //定義四個(gè)觀察者對(duì)象
            IObserver player1, player2, player3, player4;

            player1 = new Player("楊過(guò)");
            acc.Register(player1);

            player2 = new Player("令狐沖");
            acc.Register(player2);

            player3 = new Player("張無(wú)忌");
            acc.Register(player3);

            player4 = new Player("段譽(yù)");
            acc.Register(player4);

            //某成員遭受攻擊
            player1.BeAttacked(acc);

            Console.ReadLine();
        }
    }

    // 抽象觀察類
    public interface IObserver
    {
        string GetName();
        void Help(); //聲明支援盟友方法
        void BeAttacked(Team acc); //聲明遭受攻擊方法
    }

    // 戰(zhàn)隊(duì)成員類:具體觀察者類
    public class Player : IObserver
    {
        private string name;

        public Player(string name)
        {
            this.name = name;
        }

        public string GetName()
        {
            return name;
        }

        // 支援盟友方法的實(shí)現(xiàn)
        public void Help()
        {
            Console.WriteLine("堅(jiān)持住耗拓," + name + "馬上來(lái)救你拇颅!");
        }

        // 遭受攻擊方法的實(shí)現(xiàn),當(dāng)遭受攻擊時(shí)將調(diào)用戰(zhàn)隊(duì)類的通知方法NotifyObserver()來(lái)通知盟友
        public void BeAttacked(Team acc)
        {
            Console.WriteLine(name + "被攻擊乔询!");
            acc.NotifyObserver(name);
        }
    }

    // 戰(zhàn)隊(duì):抽象目標(biāo)類
    public abstract class Team
    {
        protected string teamName; //戰(zhàn)隊(duì)名稱
        protected List<IObserver> players = new List<IObserver>(); //定義一個(gè)集合用于存儲(chǔ)戰(zhàn)隊(duì)成員

        public void SetTeamName(string teamName)
        {
            this.teamName = teamName;
        }

        public string GetTeamName()
        {
            return teamName;
        }

        // 注冊(cè)方法
        public void Register(IObserver obs)
        {
            Console.WriteLine(obs.GetName() + "加入" + teamName + "戰(zhàn)隊(duì)樟插!");
            players.Add(obs);
        }

        // 注銷方法
        public void Remove(IObserver obs)
        {
            Console.WriteLine(obs.GetName() + "退出" + teamName + "戰(zhàn)隊(duì)!");
            players.Remove(obs);
        }

        // 聲明抽象通知方法
        public abstract void NotifyObserver(string name);
    }

    // 具體戰(zhàn)隊(duì)類:具體目標(biāo)類
    public class ConcreteTeam : Team
    {
        public ConcreteTeam(string teamName)
        {
            Console.WriteLine(teamName + "戰(zhàn)隊(duì)組建成功!");
            Console.WriteLine("----------------------------");
            this.teamName = teamName;
        }

        // 實(shí)現(xiàn)通知方法
        public override void NotifyObserver(string name)
        {
            Console.WriteLine(teamName + "戰(zhàn)隊(duì)緊急通知黄锤,盟友" + name + "遭受敵人攻擊搪缨!");
            // 遍歷觀察者集合,調(diào)用每一個(gè)盟友(自己除外)的支援方法
            foreach (var obs in players)
            {
                if (!(obs.GetName().Equals(name, StringComparison.CurrentCultureIgnoreCase)))
                {
                    obs.Help();
                }
            }
        }
    }
}

4鸵熟、模式的優(yōu)缺點(diǎn)

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

  • 觀察者模式在觀察目標(biāo)和觀察者之間建立一個(gè)抽象的耦合副编。觀察目標(biāo)只需要維持一個(gè)抽象觀察者的集合,無(wú)須了解其具體觀察者流强。由于觀察目標(biāo)和觀察者沒(méi)有緊密地耦合在一起痹届,因此它們可以屬于不同的抽象化層次。打月。
  • 觀察者模式支持“廣播通信”队腐。主題會(huì)向所有的觀察者發(fā)出通知。
  • 觀察者模式符合“開閉原則”的要求奏篙,增加新的具體觀察者無(wú)須修改原有系統(tǒng)代碼秘通,在具體觀察者與觀察目標(biāo)之間不存在關(guān)聯(lián)關(guān)系的情況下荸型,增加新的觀察目標(biāo)也很方便稿静。

4.2 缺點(diǎn):

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

5习霹、模式適用場(chǎng)景

  • 一個(gè)抽象模型有兩個(gè)方面淋叶,其中一個(gè)方面依賴于另一個(gè)方面望门。將這些方面封裝在獨(dú)立的對(duì)象中使它們可以各自獨(dú)立地改變和復(fù)用筹误。
  • 一個(gè)對(duì)象的改變將導(dǎo)致其他一個(gè)或多個(gè)對(duì)象也發(fā)生改變,而不知道具體有多少對(duì)象將發(fā)生改變,可以降低對(duì)象之間的耦合度直晨。
  • 一個(gè)對(duì)象必須通知其他對(duì)象门烂,而并不知道這些對(duì)象是誰(shuí)。需要在系統(tǒng)中創(chuàng)建一個(gè)觸發(fā)鏈兄淫,A對(duì)象的行為將影響B(tài)對(duì)象氓润,B對(duì)象的行為將影響C對(duì)象……薯鳍,可以使用觀察者模式創(chuàng)建一種鏈?zhǔn)接|發(fā)機(jī)制。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末粹断,一起剝皮案震驚了整個(gè)濱河市瓶埋,隨后出現(xiàn)的幾起案子希柿,更是在濱河造成了極大的恐慌,老刑警劉巖养筒,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件曾撤,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡晕粪,警方通過(guò)查閱死者的電腦和手機(jī)挤悉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)巫湘,“玉大人尖啡,你說(shuō)我怎么就攤上這事∈1欤” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵盆顾,是天一觀的道長(zhǎng)怠褐。 經(jīng)常有香客問(wèn)我,道長(zhǎng)您宪,這世上最難降的妖魔是什么奈懒? 我笑而不...
    開封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮宪巨,結(jié)果婚禮上磷杏,老公的妹妹穿的比我還像新娘。我一直安慰自己捏卓,他們只是感情好极祸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般遥金。 火紅的嫁衣襯著肌膚如雪浴捆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天稿械,我揣著相機(jī)與錄音选泻,去河邊找鬼。 笑死美莫,一個(gè)胖子當(dāng)著我的面吹牛页眯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播厢呵,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼窝撵,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了述吸?” 一聲冷哼從身側(cè)響起忿族,我...
    開封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蝌矛,沒(méi)想到半個(gè)月后道批,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡入撒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年隆豹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茅逮。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡璃赡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出献雅,到底是詐尸還是另有隱情碉考,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布挺身,位于F島的核電站侯谁,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏章钾。R本人自食惡果不足惜墙贱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贱傀。 院中可真熱鬧惨撇,春花似錦、人聲如沸府寒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至纺棺,卻和暖如春榄笙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背祷蝌。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工茅撞, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人巨朦。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓米丘,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親糊啡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子拄查,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容