Home [閱讀筆記][Design Pattern] Ch25.仲介者模式(Mediator)
Post
Cancel

[閱讀筆記][Design Pattern] Ch25.仲介者模式(Mediator)

仲介者模式(Mediator)

仲介者模式(Mediator)又叫做調停者模式。其實就是中間人或者調停的意思。

用一個仲介物件來封裝一系列的物件互動。仲介者使各物件間不需要顯式地互相參考,從而使其耦合鬆散,而且可以獨立地改變它們之間的互動。

比如,公司IT部門的管理為例,當新進員工去尋求任何一個不認識的IT部門同事幫忙是有困難的,但如果有個IT主管來協調工作,主管就是一個「仲介者」物件了。

仲介者模式一般應用於一組物件以定義良好但是複雜的方式進行通訊的場合,比如表單Form或Web頁面的aspx,以及想訂製一個分佈在多個類別中的行為,而又不想產生太多子類別的場合。

典型的仲介者模式應用:.NET 寫得 Windows應用程式中的 Form,Web網站程式的 aspx 就是典型的仲介者。

比如,小算盤程式,每個控制元件的類別程式碼都被封裝了,所以它們的實體是不會知道其他控制元件物件的存在,它們都有事件機制,而事件的執行都是在 Form表單的程式碼中完成,也就是說有的控制元件的互動都是由Form表單來作仲介,操作各個物件。

結構

  • Mediator 抽象仲介者,定義了同事物件到仲介者物件的介面
  • Colleague 抽象同事類別
  • ConcreteColleague 具體同事類別,每個具體同事只知道自己的行為,而不瞭解其他同事類別的情況,但它們卻都認識仲介者物件
  • ConcreteMediator 具體仲介物件,實現抽象類別的方法,它需要知道所有具體同事類別,並從具體同事接收消息,向具體同事物件發出命令
1
2
3
4
5
6
7
8
9
10
Mediator 抽象仲介者,定義了同事物件到仲介者物件的介面

    Colleague 抽象同事類別
    - mediator

        ConcreteColleague1 具體同事類別,每個具體同事只知道自己的行為,而不瞭解其他同事類別的情況,但它們卻都認識仲介者物件

        ConcreteColleague2

    ConcreteMediator 具體仲介物件,實現抽象類別的方法,它需要知道所有具體同事類別,並從具體同事接收消息,向具體同事物件發出命令

程式碼

Mediator 抽象仲介者

Mediator 抽象仲介者,定義了同事物件到仲介者物件的介面。

1
2
3
abstract class Mediator {
    public abstract void Send(string message, Colleague colleague);
}

Colleague 抽象同事類別

Colleague 抽象同事類別。

1
2
3
4
5
6
7
abstract class Colleague {
    protected Mediator mediator;
    //建構式,得到仲介者物件
    public Colleague(Mediator mediator) {
        this.mediator = mediator;
    }
}

ConcreteMediator 具體仲介物件

ConcreteMediator 具體仲介物件,實現抽象類別的方法,它需要知道所有具體同事類別,並從具體同事接收消息,向具體同事物件發出命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
class ConcreteMediator: Mediator {
    pubilc ConcreteColleague1 ConcreteColleague1 {set;}
    pubilc ConcreteColleague2 ConcreteColleague2 {set;}

    //重寫發送資訊的方法,根據物件做出選擇判斷,通知物件
    public override void Send(string message, Colleague colleague) {
        if(colleague == ConcreteColleague1) {
            ConcreteColleague2.Notify(message);
        } else {
            ConcreteColleague1.Notify(message);
        }
    }
}

ConcreteColleague 具體同事類別

ConcreteColleague 具體同事類別,每個具體同事只知道自己的行為,而不瞭解其他同事類別的情況,但它們卻都認識仲介者物件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//具體同事1
class ConcreteColleague1: Colleague {
    public ConcreteColleague1(Mediator mediator): base(mediator) { }

    public void Send(string message) {
        mediator.Send(message, this);
    }

    public void Notify(string message) {
        Console.WriteLine($"同事1得到資訊: {message}");
    }
}

//具體同事2
class ConcreteColleague2: Colleague {
    public ConcreteColleague2(Mediator mediator): base(mediator) { }

    public void Send(string message) {
        mediator.Send(message, this);
    }

    public void Notify(string message) {
        Console.WriteLine($"同事1得到資訊: {message}");
    }
}

用戶端調用

有了MediatorConcreteColleague1ConcreteColleague2在發送消息和接收資訊時,其實都是透過仲介者來完成,這就減少了它們之間的耦合度了。

1
2
3
4
5
6
7
8
9
10
11
12
ConcreteMediator m = new ConcreteMediator();

ConcreteColleague1 c1 = new ConcreteColleague1(m);
ConcreteColleague2 c2 = new ConcreteColleague2(m);

//讓仲介者認識各個具體同事類物件
m.colleague1 = c1;
m.colleague2 = c2;

//具體同事類物件的發送資訊,都是透過仲介者轉發
c1.Send("吃過飯了?");
c2.Send("還沒呢,有人要請客嗎?");

安理會做仲介

結構

1
2
3
4
5
6
聯合國機構 (相當於Mediator類別)
    聯合國安理會 (相當於ConcreteMediator類別)

國家 (相當於Colleague類別)
    美國 (相當於ConcreteColleague1類別)
    伊拉克 (相當於ConcreteColleague2類別)

程式碼

聯合國機構 (相當於Mediator類別)

1
2
3
abstract class UnitedNations {
    public abstract void Declare(string message, Country colleague);
}

聯合國安理會 (相當於ConcreteMediator類別)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class UnitedNationsSecurityCouncil: UnitedNations {
    //聯合國安理會瞭解所有的國家,所以擁有美國和伊拉克的物件屬性
    public USA Colleague1 { get; set; }
    public Iraq Colleague2 { get; set; }

    //「表態/宣告/發言」方法,實現了兩個物件的通訊
    public override void Declare(string message, Country colleague) {
        if(Colleague == Colleague1) {
            Colleague2.GetMessage(message);
        } else {
            Colleague1.GetMessage(message);
        }
    }
}

國家 (相當於Colleague類別)

1
2
3
4
5
6
abstract class Country {
    protected UnitedNations mediator;
    public Country(UnitedNations mediator) {
        this.mediator = mediator;
    }
}

美國 (相當於ConcreteColleague1類別)

1
2
3
4
5
6
7
8
9
10
11
12
13
class USA: Country {
    public USA(UnitedNations mediator): base(mediator) { }

    //表態、發言
    public void Declare(string message) {
        mediator.Declare(message, this);
    }

    //獲得消息
    public void GetMessage(string message) {
        Console.WriteLine($"美國獲得對方資訊: {message}");
    }
}

伊拉克 (相當於ConcreteColleague2類別)

1
2
3
4
5
6
7
8
9
10
11
12
13
class Iraq: Country {
    public Iraq(UnitedNations mediator): base(mediator) { }

    //表態、發言
    public void Declare(string message) {
        mediator.Declare(message, this);
    }

    //獲得消息
    public void GetMessage(string message) {
        Console.WriteLine($"伊拉克獲得對方資訊: {message}");
    }
}

用戶端調用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
UnitedNationsSecurityCouncil unsc = new UnitedNationsSecurityCouncil();

USA c1 = new USA(unsc);
Iraq c2 = new Iraq(unsc);

unsc.Colleague1 = c1;
unsc.Colleague2 = c2;

c1.Declare("不准研製核武器,否則要發動戰爭!");
c2.Declare("我們沒有核武器,也不怕侵略。");

/* 執行結果:

伊拉克獲得對方資訊: 不准研製核武器,否則要發動戰爭!
美國獲得對方資訊: 我們沒有核武器,也不怕侵略。
*/

優缺點

如果聯合國安理會出了問題,當然會對世界都造成影響。所以說,仲介模式很容易在系統中應用,也很容易在系統中誤用。當系統出現了「多對多」互動複雜的物件群時,不要急於使用仲介模式,而要先反省你的系統在設計上是不是合理。

優點

Mediator的出現減少了各個Colleague的耦合,所以可以獨立地改變和複用各個Colleague類別和Mediator

比如,任何國家的改變不會影響到其他國家,而只是與安理會發生變化。

其次,由於把物件如何協作進行了抽象,將仲介作為一個獨立的概念並將其封裝在一個物件中,這樣焦點的物件就從物件各自本身的行為轉移到它們之間的互動上來,也就是站在一個更宏觀的角度去看待系統。

比如,以巴衝突,本來只能算是國與國之間的矛盾,因此各自的看法可能都比較狹隘,但站在聯合國安理會的角度,就可以從全球化、也更客觀的角度來看待問題,在調停和維和上做出貢獻。

缺點

具體仲介者類別ConcreteColleague可能會因為ConcreteColleague的越來越多,而變得非常複雜,反而不容易維護了。

儘管這樣的設計可以減少ConcreteColleague類別之間的耦合,但這又讓ConcreteMediator責任太多了,如果它出了問題,則整個系統都會有問題了。

This post is licensed under CC BY 4.0 by the author.

[閱讀筆記][Design Pattern] Ch24.職責鏈模式(Chain of Responsibility)

[閱讀筆記][Design Pattern] Ch26.享元模式(Flyweight)