設計模式之中介者模式(十一)


一、引出模式

在生活中,當電腦缺少了一塊主板,那會怎么樣?如果有人這樣問我的話,我就會馬上跳出來說“這電腦肯定報廢了”,當然這不是重點。假如少了主板電腦還可以用的話,想想,里面的CPU、顯卡、聲卡、光驅、硬盤等等,不是就要我們自己用線把它們連起來。想想就覺得頭疼,那么現在你覺得主板在電腦里扮演着什么角色呢?

在軟件的開發過程中,勢必會碰到這樣一種情況,多個類或多個子系統相互交互,而且交互很繁瑣,導致每個類都必須知道他需要交互的類,這樣它們的耦合會顯得異常厲害。牽一發而動全身又不木有啊!

好了,既然問題提出來了,那有請我們這期的主角——中介者模式出場吧!

二、認識模式

1.模式定義

用一個中介者對象來封裝一系列的對象交互。中介者使得各對象不需要顯式地相互引用,從而使其松散耦合,而且可以獨立地改變它們之間的交互。

2.解決思路

出現問題的根本原因在於多個對象需要相互交互,導致了緊密耦合,不利於對象的修改和維護。

中介者模式的解決思路,就和“主板”一樣,通過引入一個中介對象,讓其他對象都只和這個中介者對象交互,而中介者對象是知道怎樣和其他對象交互。這樣一來,對象直接的交互關系就沒有了,從而實現了松散耦合。

對於中介對象而言,所有相互交互的對象,都視為同事類,中介對象就是用來維護各個同事對象之間的關系,所有的同事類都只和中介對象交互,也就是說,中介對象是需要知道所有的同事對象的。

當一個同事對象自身發生變化時,它是不知道會對其他同事對象產生什么影響,它只需要通知中介對象,“我發生變化了”,中介對象會去和其他同事對象進行交互的。這樣一來,同事對象之間的依賴就沒有了。

有了中介者之后,所有的交互都封裝在了中介對象里面,各個對象只需要關心自己能做什么就行,不需要再關系做了之后會對其他對象產生什么影響,也就是無需再維護這些關系了。

3.模式結構

  

Mediator:中介者接口。在里面定義了各個同事之間相互交互所需要的方法,可以是公共的方法,如Change方法,也可以是小范圍的交互方法。

ConcreteMediator:具體的中介者實現對象。它需要了解並為維護每個同事對象,並負責具體的協調各個同事對象的交互關系。

Colleague:同事類的定義,通常實現成為抽象類,主要負責約束同事對象的類型,並實現一些具體同事類之間的公共功能,比如,每個具體同事類都應該知道中介者對象,也就是每個同事對象都會持有中介者對象的引用,這個功能可定義在這個類中。

ConcreteColleague:具體的同事類,實現自己的業務,需要與其他同事對象交互時,就通知中介對象,中介對象會負責后續的交互。

4.模式示例代碼

internal class Program

    {
        private static void Main(string[] args)
        {

        }
    }


    /// <summary>
    /// 同事類的抽象父類
    /// </summary>
    public abstract class Colleague
    {
        /// <summary>
        /// 持有中介者對象,每一個同事類都知道它的中介者對象
        /// </summary>
        private Mediator mediator;

        /// <summary>
        /// 構造方法,傳入中介者對象
        /// </summary>
        /// <param name="mediator">中介者對象</param>
        protected Colleague(Mediator mediator)
        {
            this.mediator = mediator;
        }

        /// <summary>
        /// 獲取當前同事類對應的中介者對象
        /// </summary>
        /// <returns>對應的中介者對象</returns>
        public Mediator GetMediator()
        {
            return mediator;
        }
    }

    /// <summary>
    /// 具體的同事類A
    /// </summary>

    public class ConcreteColleagueA : Colleague
    {
        /// <summary>
        /// 調用父類的構造函數
        /// </summary>
        /// <param name="mediator"></param>
        public ConcreteColleagueA(Mediator mediator):base(mediator)
        {

        }

 
        /// <summary>
        /// 執行某些業務功能
        /// </summary>
        public void SomeOperation()
        {
            //在需要跟其他同事通信的時候,通知中介者對象
            base.GetMediator().Change(this);
        }

    }

 
    /// <summary>
    /// 具體的同事類B
    /// </summary>
    public class ConcreteColleagueB : Colleague
    {
        /// <summary>
        /// 調用父類的構造函數
        /// </summary>
        /// <param name="mediator"></param>
        public ConcreteColleagueB(Mediator mediator)
            : base(mediator)

        {

        }

        /// <summary>
        /// 執行某些業務功能
        /// </summary>
        public void SomeOperation()
        {
            //在需要跟其他同事通信的時候,通知中介者對象
            base.GetMediator().Change(this);
        }

    }

 
    /// <summary>
    /// 中介者,定義各個同事對象通信的接口
    /// </summary>
    public interface Mediator
    {
        void Change(Colleague colleague);
    }
 
    /// <summary>
    /// 具體的中介者實現
    /// </summary>
    public class ConcreteMediator
    {
        /// <summary>
        /// 持有並維護同事A
        /// </summary>
        private ConcreteColleagueA colleagueA;
 
        /// <summary>
        /// 持有並維護同事B
        /// </summary>
        private ConcreteColleagueB colleagueB;
 

        /// <summary>
        /// 設置中介者需要了解並維護的同事A對象
        /// </summary>
        /// <param name="colleague">同事A對象</param>
        public void SetConcreteColleagueA(ConcreteColleagueA colleague)
        {
            colleagueA = colleague;
        }

 
        /// <summary>
        /// 設置中介者需要了解並維護的同事B對象
        /// </summary>
        /// <param name="colleague">同事B對象</param>
        public void SetConcreteColleagueB(ConcreteColleagueB colleague)
        {
            colleagueB = colleague;
        }

        public void Changed(Colleague colleague)
        {
            //某個同事類發生了變化,通常需要與其他同事交互
            //具體協調相應的同事對象來實現協作行為
        }

    }

 

三、理解模式

1.中介者模式的功能

中介者的功能就是封裝對象之間的交互。如果一個對象的操作會引起其他相關對象的變化,而這個對象又不希望自己來處理這些關系,那么就可以去找中介者,讓它來處理這些麻煩的關系。

2.需要Mediator接口嗎?

這一問題,首先得弄清楚接口用來干嘛的?接口是用來“封裝隔離”的,Mediator接口用來封裝中介者對象,使得中介者對象的客戶對象跟具體的中介者實現對象分離開。

如果只有一個中介者對象,預期中也沒打算擴展,那就可以不定義Mediator接口。

3.同事關系

在標准的中介者模式中,將使用中介者對象來交互的那些對象稱為同事類,這是要求這些類都要繼承自相同的類,也就是說它們算是兄弟對象。

4.同事和中介者的關系

在模式中,當一個同事對象發生變化時,需要主動通知中介者,讓中介者去處理和其他同事對象的相關交互。

這就導致同事對象與中介對象必須有關系,同事對象需要知道中介對象,以便通知;中介對象需要知道同事對象,一邊交互。所有它們的關系是相互依賴的。

5.如何實現同事和中介者的通信

一種方式是在Mediator接口中定義一個特殊的通知接口,作為一個通用的方法,讓各個同事來調用這個方法。

另一種就是采用觀察者模式,把Mediator實現成為觀察者,各個同事對象實現為Subject.這些同事對象發生改變時,就會通知Mediator

6.廣義中介者

在實際開發中,經常會簡化中介者模式,來是開發變得簡單,比如有如下的簡化。

l 通常會去掉同事對象的父類,這樣可以讓任意的對象,只需要有交互,就可以成為同事

l 通常不定義Mediator接口,把具體的中介者對象實現成為單例

l 同事對象不再持有中介者,而是在需要的時候直接獲取中介者對象並調用;中介者也不再持有同事對象,而是在具體處理方法里面去創建,或獲取,或從數據傳入需要的同事對象。

經過這樣的簡化、變形的情況稱為廣義中介者。

7.何時選用中介者模式

如果一組對象之間的通信方式比較復雜,導致相互依賴,結構混亂,可以采用中介者模式

如果一個對象引用很多對象,並且跟這些對象交互,導致難以服用該對象

8.模式本質

中介者模式的本質在於“封裝交互”

中介者模式的目的,就是封裝多個對象的交互,這些交互的多在中介者對象里實現。

只要是實現封裝對象的交互,就可以使用中介者模式,不必拘泥於模式結構。

9.相關模式

中介者模式和外觀模式

外觀模式多用於封裝一個子系統內部的多個模塊,目的是向子系統外部提供簡單易用的接口。

中介者模式是提供多個平等的同事對象之間交互關系的封裝,一般是用在內部實現上。

中介者模式和觀察者模式

這兩個模式可以組合使用。

中介者模式可以組合使用觀察者模式,來實現當同事對象發生改變時,通知中介對象,讓中介對象去進行與其他相關對象的交互。

 最后是一個中介者模式的實例:打開電腦看電影

比如有如下的交互:

1.首先光驅讀取光盤上的數據,然后告訴主板,它的狀態已經改變了。

2.主板得到光驅的數據,將數據交給CPU進行分析處理。

3.CPU處理完,將數據分成了視頻數據和音頻數據,通知主板,已將處理好了。

4.主板得到數據,將數據交給顯卡和聲卡進行輸出。

代碼示例:

class Program
    {
        static void Main(string[] args)
        {
            //1.創建中介者——主板對象
            ConcreteMediator mediator = new ConcreteMediator();

            //2.創建同事類
            CDDriver cd = new CDDriver(mediator);
            CPU cpu = new CPU(mediator);
            VideoCard videocard = new VideoCard(mediator);
            SoundCard soundcard = new SoundCard(mediator);
            //3.讓中介知道所有的同事
            mediator.SetCDDriver(cd);
            mediator.SetCPU(cpu);
            mediator.SetVideoCard(videocard);
            mediator.SetSoundCard(soundcard);

            //4.開始看電影
            cd.ReadCD();

            Console.Read();

        }
    }

    #region 同事抽象類

    /// <summary>
    /// 同事抽象類
    /// </summary>
    public abstract class Colleague
    {
        //持有中介者對象的引用,因為每個同事類都應該知道中介者對象
        private Mediator mediator;

        //構造函數,傳入中介者對象
        public Colleague(Mediator mediator)
        {
            this.mediator = mediator;
        }

        //得到當前同事類的中介者對象
        public Mediator GetMediator()
        {
            return this.mediator;
        }
    } 
    #endregion

    #region 同事具體類
    /// <summary>
    /// 光驅類
    /// </summary>
    public class CDDriver : Colleague
    {
        public CDDriver(Mediator mediator)
            : base(mediator)
        {

        }

        /// <summary>
        /// 光驅讀取出來的數據
        /// </summary>
        private string Data = null;

        /// <summary>
        /// 獲取光驅讀取出來的數據
        /// </summary>
        /// <returns></returns>
        public string GetData()
        {
            return this.Data;
        }

        public void ReadCD()
        {
            //逗號前是視頻數據,逗號后是聲音數據
            this.Data = "這是視頻數據,這是聲音數據";

            //通知主板,自己的狀態反生了改變
            this.GetMediator().Change(this);
        }
    }

    /// <summary>
    /// CPU類
    /// </summary>
    public class CPU : Colleague
    {
        public CPU(Mediator mediator)
            : base(mediator)
        {

        }

        /// <summary>
        /// 分解出來的視頻數據
        /// </summary>
        private string videioData = null;

        /// <summary>
        /// 分解出來的視頻數據
        /// </summary>
        private string soundData = null;

        /// <summary>
        /// 獲取分解出來的視頻數據
        /// </summary>
        /// <returns></returns>
        public string GetVideioData()
        {
            return this.videioData;
        }

        /// <summary>
        /// 獲取分解出來的聲音數據
        /// </summary>
        /// <returns></returns>
        public string GetSoundData()
        {
            return this.soundData;
        }

        /// <summary>
        /// 處理數據
        /// </summary>
        public void ExecuteData(string data)
        {
            string[] ss = data.Split(',');
            this.videioData = ss[0];
            this.soundData = ss[1];

            //通知主板,CPU工作已完成
            this.GetMediator().Change(this);
        }
    }

    /// <summary>
    /// 顯卡類
    /// </summary>
    public class VideoCard : Colleague
    {
        public VideoCard(Mediator mediator)
            : base(mediator)
        {

        }

        /// <summary>
        /// 顯示視頻數據源
        /// </summary>
        public void ShowData(string data)
        {
            Console.WriteLine("您正在看:" + data);
        }
    }

    /// <summary>
    /// 聲卡類
    /// </summary>
    public class SoundCard : Colleague
    {
        public SoundCard(Mediator mediator)
            : base(mediator)
        {

        }

        /// <summary>
        /// 顯示聲音數據源
        /// </summary>
        public void ShowData(string data)
        {
            Console.WriteLine("您正在聽:" + data);
        }
    } 
    #endregion

    #region 中介者抽象類

    /// <summary>
    /// 中介者抽象類
    /// </summary>
    public abstract class Mediator
    {
        public abstract void Change(Colleague colleague);
    } 
    #endregion

    #region 中介者具體類

    /// <summary>
    /// 中介者具體類
    /// </summary>
    public class ConcreteMediator : Mediator
    {
        private CDDriver cdDriver;
        private CPU cpu;
        private VideoCard video;
        private SoundCard sound;

        public void SetCDDriver(CDDriver cdDriver)
        {
            this.cdDriver = cdDriver;
        }

        public void SetCPU(CPU cpu)
        {
            this.cpu = cpu;
        }

        public void SetVideoCard(VideoCard video)
        {
            this.video = video;
        }

        public void SetSoundCard(SoundCard sound)
        {
            this.sound = sound;
        }

        public override void Change(Colleague colleague)
        {
            if (colleague == cdDriver)
            {
                openCDAndReadData((CDDriver)colleague);
            }
            else if (colleague == cpu)
            {
                OpenCPU((CPU)colleague);
            }
        }

        /// <summary>
        /// 打開CD,並讀取數據
        /// </summary>
        /// <param name="cs"></param>
        private void openCDAndReadData(CDDriver cs)
        {
            //獲取光驅讀取的數據
            string data = cdDriver.GetData();
            //把這些數據傳遞給CPU進行處理
            this.cpu.ExecuteData(data);
        }

        /// <summary>
        /// CPU處理
        /// </summary>
        /// <param name="cpu"></param>
        private void OpenCPU(CPU cpu)
        {
            //獲取數據
            string videoData = cpu.GetVideioData();
            string soundData = cpu.GetSoundData();

            //顯示數據
            this.video.ShowData(videoData);
            this.sound.ShowData(soundData);

        }
    } 
    #endregion

 

 


免責聲明!

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



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