第八節:觀察者模式和中介者模式詳解


一. 觀察者模式

1. 背景

  在現實世界中,許多對象並不是獨立存在的,其中一個對象的行為發生改變可能會導致一個或者多個其他對象的行為也發生改變。例如,某種商品的物價上漲時會導致部分商家高興,而消費者傷心;還有,當我們開車到交叉路口時,遇到紅燈會停,遇到綠燈會行。這樣的例子還有很多,例如,股票價格與股民、微信公眾號與微信用戶、氣象局的天氣預報與聽眾、小偷與警察等。

  在軟件世界也是這樣,例如,Excel 中的數據與折線圖、餅狀圖、柱狀圖之間的關系;MVC 模式中的模型與視圖的關系;事件模型中的事件源與事件處理者。所有這些,如果用觀察者模式來實現就非常方便。

2. 定義和特點

(1). 定義

 指多個對象間存在一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴於它的對象都得到通知並被自動更新。這種模式有時又稱作發布-訂閱模式模型-視圖模式,它是對象行為型模式。

PS: 觀察者模式是 【1對多】的關系,后面的中介者模式是 【多對多】的關系。

(2). 優點:

 A. 降低了目標與觀察者之間的耦合關系,兩者之間是抽象耦合關系。

 B. 目標與觀察者之間建立了一套觸發機制。

(3). 缺點:

 A. 目標與觀察者之間的依賴關系並沒有完全解除,而且有可能出現循環引用。

 B. 當觀察者對象很多時,通知的發布會花費很多時間,影響程序的效率。

3. 具體實現

(1). 模式結構

 實現觀察者模式時要注意具體目標對象和具體觀察者對象之間不能直接調用,否則將使兩者之間緊密耦合起來,這違反了面向對象的設計原則。

 A. 抽象目標類:它提供了一個用於保存觀察者對象的聚集類和增加、刪除觀察者對象的方法,以及通知所有觀察者的抽象方法。

 B. 具體目標類:也叫具體目標類,它實現抽象目標中的通知方法,當具體主題的內部狀態發生改變時,通知所有注冊過的觀察者對象。

 C. 抽象觀察者:它是一個抽象類或接口,它包含了一個更新自己的抽象方法,當接到具體主題的更改通知時被調用。

 D. 具體觀察者:實現抽象觀察者中定義的抽象方法,以便在得到目標的更改通知時更新自身的狀態。

結構圖如下:

(2). 使用場景

   老師向學生發通知,老師是具體的目標類,學生是具體的觀察者。

(3). 代碼實操

抽象觀察者和具體的觀察者(學生類)

    /// <summary>
    /// 抽象觀察者
    /// </summary>
    public interface IObserver
    {

       /// <summary>
       /// 接收通知
       /// </summary>
       /// <param name="msg"></param>
      void  ReceiveNotice(string msg);

    }
     /// <summary>
    /// 學生1
    /// (具體觀察者)
    /// </summary>
    public class StudentObserver1 : IObserver
    {
        public void ReceiveNotice(string msg)
        {
            Console.WriteLine($"學生1已經收到通知,內容為:{msg}");
        }
    }
     /// <summary>
    /// 學生2
    /// (具體觀察者)
    /// </summary>
    public class StudentObserver2 : IObserver
    {
        public void ReceiveNotice(string msg)
        {
            Console.WriteLine($"學生2已經收到通知,內容為:{msg}");
        }
    }
View Code

抽象目標類和具體目標類(老師類)

 /// <summary>
    /// 抽象目標
    /// (定義操作觀察者的相關方法)
    /// </summary>
    public abstract class AbstractSub
    {
        protected List<IObserver> obList = new List<IObserver>();

        /// <summary>
        /// 增加觀察者
        /// </summary>
        /// <param name="ob"></param>
        public void AddOb(IObserver ob)
        {
            if (!obList.Contains(ob))
            {
                obList.Add(ob);
            }
            
        }

        /// <summary>
        /// 刪除觀察者
        /// </summary>
        /// <param name="ob"></param>
        public void RemoveOb(IObserver ob)
        {
            if (obList.Contains(ob))
            {
                obList.Remove(ob);
            }       
        }

        /// <summary>
        /// 通知具體觀察者的方法
        /// </summary>
        public abstract void notifyObserver(string msg);

    }

    /// <summary>
    /// 老師1
    /// 具體目標(發通知  發布者)
    /// </summary>
    public class TeacherSub1 : AbstractSub
    {
        /// <summary>
        /// 實現通知學生的業務
        /// </summary>
        public override void notifyObserver(string msg)
        {
            foreach (var item in obList)
            {
                //向每個學生(即觀察者)發送通知
                //當觀察者較多的時候,此處考慮使用線程池
                item.ReceiveNotice(msg);
            }
        }
    }
View Code

測試

            {
                //1. 聲明所有學生類(觀察者)
                IObserver student1 = new StudentObserver1();
                IObserver student2 = new StudentObserver2();

                //2. 聲明老師類(發布者)
                AbstractSub teacher1 = new TeacherSub1();
                teacher1.AddOb(student1);
                teacher1.AddOb(student2);

                //3.發送通知
                teacher1.notifyObserver("2020-08-13, 晚上8點全體開會哦");
            }

運行結果

 4. 適用場景分析 

(1). 對象間存在一對多關系,一個對象的狀態發生改變會影響其他對象。

(2). 當一個抽象模型有兩個方面,其中一個方面依賴於另一方面時,可將這二者封裝在獨立的對象中以使它們可以各自獨立地改變和復用。

 

二. 中介者模式

1. 背景

  在現實生活中,常常會出現好多對象之間存在復雜的交互關系,這種交互關系常常是“網狀結構”,它要求每個對象都必須知道它需要交互的對象。例如,每個人必須記住他(她)所有朋友的電話;而且,朋友中如果有人的電話修改了,他(她)必須告訴其他所有的朋友修改,這叫作“牽一發而動全身”,非常復雜。

  如果把這種“網狀結構”改為“星形結構”的話,將大大降低它們之間的“耦合性”,這時只要找一個“中介者”就可以了。如前面所說的“每個人必須記住所有朋友電話”的問題,只要在網上建立一個每個朋友都可以訪問的“通信錄”就解決了。這樣的例子還有很多,例如,你剛剛參力口工作想租房,可以找“房屋中介”;或者,自己剛剛到一個陌生城市找工作,可以找“人才交流中心”幫忙。

  在軟件的開發過程中,這樣的例子也很多,例如,在 MVC 框架中,控制器(C)就是模型(M)和視圖(V)的中介者;還有大家常用的 QQ 聊天程序的“中介者”是 QQ 服務器。所有這些,都可以采用“中介者模式”來實現,它將大大降低對象之間的耦合性,提高系統的靈活性。

2. 定義和特點

(1). 定義

 定義一個中介對象來封裝一系列對象之間的交互,使原有對象之間的耦合松散,且可以獨立地改變它們之間的交互。中介者模式又叫調停模式,它是迪米特法則的典型應用

PS:中介者模式是 【多對多】的關系,上面的觀察者模式是 【1對多】的關系。

(2). 優點

 A. 降低了對象之間的耦合性,使得對象易於獨立地被復用。

 B. 將對象間的多對多關聯轉變為一對一的關聯,提高系統的靈活性,使得系統易於維護和擴展。

(3). 缺點

 同事類太多時,中介者的職責將很大,它會變得復雜而龐大,以至於系統難以維護

3. 具體實現

(1). 模式結構

 A. 抽象中介者:它是中介者的接口,提供了同事對象注冊與轉發同事對象信息的抽象方法。

 B. 具體中介者:實現中介者接口,定義一個 List 來管理同事對象,協調各個同事角色之間的交互關系,因此它依賴於同事角色。

 C. 抽象同事類:定義同事類的接口,保存中介者對象,提供同事對象交互的抽象方法,實現所有相互影響的同事類的公共功能。

 D. 具體同事類:是抽象同事類的實現者,當需要與其他同事對象交互時,由中介者對象負責后續的交互。

結構圖如下:

(2). 使用場景

   一個公司中,不同部門的員工不允許直接對接,這個時候需要一個中間人,幫着溝通聯系,這個中間人就是中介者。

(3). 代碼實操

抽象中介者和具體中介者

    /// <summary>
    /// 抽象中介者
    /// </summary>
   public abstract class AbstractMediator
    {
        //添加同事
        public abstract void AddColleague(AbstractColleague colleague);

        //消息轉發
        public abstract void TransMsg(AbstractColleague cl,string msg); //轉發
    }
  /// <summary>
    /// 具體中介者
    /// (用於協調轉發各部門同事的信息傳輸)
    /// </summary>
    public class Mediator1 : AbstractMediator
    {
        private List<AbstractColleague> cList = new List<AbstractColleague>();

        /// <summary>
        /// 添加同事
        /// </summary>
        /// <param name="colleague"></param>
        public override void AddColleague(AbstractColleague colleague)
        {
            if (!cList.Contains(colleague))
            {
                cList.Add(colleague);
            }
        }

        /// <summary>
        /// 消息轉發(1對1)
        /// </summary>
        /// <param name="cl"></param>
        /// <param name="msg"></param>
        public override void TransMsg(AbstractColleague cl, string msg)
        {
            foreach (var item in cList)
            {
                if (item.Equals(cl))
                {
                    item.ReceiveMsg(msg);
                }
            }
        }
    }
View Code

抽象同事類和具體同事類

/// <summary>
    /// 抽象同事類
    /// </summary>
    public abstract class AbstractColleague
    {
        protected AbstractMediator _abstractMediator;

        public AbstractColleague(AbstractMediator abstractMediator)
        {
            this._abstractMediator = abstractMediator;
        }

        /// <summary>
        /// 收消息
        /// </summary>
        /// <param name="msg"></param>
        public abstract void ReceiveMsg(string msg);

        /// <summary>
        /// 發消息(這里指1對1)
        /// </summary>
        /// <param name="msg"></param>
        public abstract void SendMsg(AbstractColleague c, string msg);

    }
 /// <summary>
    /// 同事1(位於業務部門)
    /// </summary>
    public class Colleague1 : AbstractColleague
    {
        public Colleague1(AbstractMediator meditor):base(meditor)
        {

        }

        public override void ReceiveMsg(string msg)
        {
            Console.WriteLine($"同事1收到新消息,內容為:{msg}");
        }

        public override void SendMsg(AbstractColleague c, string msg)
        {
            //調用中介者進行消息轉發
            _abstractMediator.TransMsg(c, msg);
        }
    }
 /// <summary>
    /// 同事2(位於技術部門)
    /// </summary>
   public class Colleague2 : AbstractColleague
    {
        public Colleague2(AbstractMediator meditor) : base(meditor)
        {

        }

        public override void ReceiveMsg(string msg)
        {
            Console.WriteLine($"同事2收到新消息,內容為:{msg}");
        }

        public override void SendMsg(AbstractColleague c, string msg)
        {
            //調用中介者進行消息轉發
            _abstractMediator.TransMsg(c, msg);
        }
    }
 /// <summary>
    /// 同事3,位於行政部門
    /// </summary>
    public class Colleague3 : AbstractColleague
    {
        public Colleague3(AbstractMediator meditor) : base(meditor)
        {

        }

        public override void ReceiveMsg(string msg)
        {
            Console.WriteLine($"同事3收到新消息,內容為:{msg}");
        }

        public override void SendMsg(AbstractColleague c, string msg)
        {
            //調用中介者進行消息轉發
            _abstractMediator.TransMsg(c, msg);
        }
    }
View Code

測試

  {
                //1.聲明中介者
                AbstractMediator aMediator = new Mediator1();

                //2. 聲明各業務同事
                AbstractColleague c1 = new Colleague1(aMediator);
                AbstractColleague c2 = new Colleague2(aMediator);
                AbstractColleague c3 = new Colleague3(aMediator);

                //3.給中介者添加各個同事
                aMediator.AddColleague(c1);
                aMediator.AddColleague(c2);
                aMediator.AddColleague(c3);

                //4.開始信息交流
                Console.WriteLine("c1發消息給c2");
                c1.SendMsg(c2, "今晚一起吃飯吧");

                Console.WriteLine("c2發消息給c3");
                c3.SendMsg(c3, "明天8點一起開會");

}

運行結果

 4. 適用場景分析 

(1). 當對象之間存在復雜的網狀結構關系而導致依賴關系混亂且難以復用時。

(2). 當想創建一個運行於多個類之間的對象,又不想生成新的子類時。

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鵬飛)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 聲     明1 : 如有錯誤,歡迎討論,請勿謾罵^_^。
  • 聲     明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM