
我們都用過QQ,它有兩種聊天方式:一是私聊,二是群聊。使用QQ群,一個使用者就可以向多個使用者發送相同的資訊和檔案,進而無需一一發送,節省大量時間。通過引入群的機制,極大地減少系統中使用者之間的兩兩通信,使用者與使用者之間的聯系可以通過群的機制來實作。在有些軟體中,某些類/對象之間的互相調用關系錯綜複雜,類似于QQ使用者之間的關系,此時,特别需要一個類似“QQ群”一樣的中間類來協調這些類/對象之間的複雜關系,以降低系統的耦合度。是以,一個設計模式是以誕生,它就是中介者模式。
我們都用過QQ,它有兩種聊天方式:一是私聊,二是群聊。使用QQ群,一個使用者就可以向多個使用者發送相同的資訊和檔案,進而無需一一發送,節省大量時間。通過引入群的機制,極大地減少系統中使用者之間的兩兩通信,使用者與使用者之間的聯系可以通過群的機制來實作。
在有些軟體中,某些類/對象之間的互相調用關系錯綜複雜,類似于QQ使用者之間的關系,此時,特别需要一個類似“QQ群”一樣的中間類來協調這些類/對象之間的複雜關系,以降低系統的耦合度。是以,一個設計模式是以誕生,它就是中介者模式。
中介者模式(Mediator) | 學習難度:★★★☆☆ | 使用頻率:★★☆☆☆ |
一、客戶資訊管理子產品的初始設計
1.1 需求背景
Background:M公司欲開發一套CRM系統,其中包含一個客戶資訊管理子產品,所涉及的“客戶資訊管理視窗”界面效果圖如下圖所示:M公司開發人員通過分析發現,在上圖中,界面元件之間存在較為複雜的互動關系:如果删除一個客戶,則将從客戶清單中删掉對應的項,客戶選擇組合框中客戶名稱也稱将減少一個;如果增加一個客戶資訊,則客戶清單中将增加一個客戶,且組合框中也将增加一項。![]()
設計模式的征途—22.中介者(Mediator)模式
1.2 初始設計
M公司開發人員針對元件之間的互動關系進行了分析,發現:
(1)當使用者單擊“增加”、“删除”、“修改”或“查詢”時,界面左側的“客戶選擇組合框”、“客戶清單”以及界面中的文本框将産生響應。
(2)當使用者通過”客戶選擇組合框“選中某個客戶姓名時,”客戶清單“和文本框将産生響應。
(3)當使用者通過“客戶清單”選中某個客戶姓名時,“客戶選擇組合框”和文本框将産生響應。
根據互動關系,開發人員繪制了如下圖所示的初始類圖。
不難發現,上述設計存在以下問題:
(1)系統結構負責且耦合度高 => 複雜的網狀結構,OMG!
(2)元件的可重用性差 => 由于每一個元件和其他元件之間都具有很強的關聯,很難重用!
(3)系統的擴充性差 => 如果在上述系統中增加一個新的元件類,必須修改與之互動的各個元件源代碼!
二、中介者模式概述
2.1 中介者模式簡介
如果在一個系統中對象之間存在多對多的互相關系,可以将對象之間的一些互動行為從各個對象中分離出來,并集中封裝在一個中介者對象中,并由該中介者進行統一協調,這樣對象之間多對多的複雜關系就轉化為了相對簡單的一對多關系。通過引入中介者來簡化對象之間的複雜關系,它是迪米特法則的一個典型應用。
中介者(Mediator)模式:用一個中介對象來封裝一系列的對象互動,中介者使各對象不需要顯示地互相引用,進而使其耦合松散,而且可以相對獨立地改變它們之間的互動。中介者模式又稱為調停模式,它是一種對象行為型模式。
2.2 中介者模式結構
在中介者模式中,引入了用于協調其他對象/類之間的互相調用的中介者類,為了讓系統具有更好的靈活性和可擴充性,通常還提供了抽象中介者,其結構圖如下圖所示:
中介者模式結構圖中主要包含以下4個角色:
(1)Mediator(抽象中介者):它定義了一個接口,該接口用于與各同僚對象之間進行通信。
(2)ConcreteMediator(具體中介者):它實作了接口,通過協調各個同僚對象來實作協作行為,維持了各個同僚對象的引用。
(3)Colleague(抽象同僚類):它定義了各個同僚類公有的方法,并聲明了一些抽象方法來供子類實作,同時維持了一個對抽象中介者類的引用,其子類可以通過該引用來與中介者通信。
(4)ConcreteColleague(具體同僚類):抽象同僚類的子類,每一個同僚對象需要和其他對象通信時,都需要先與中介者對象通信,通過中介者來間接完成與其他同僚類的通信。
三、客戶資訊管理子產品的重構實作
3.1 重構後的設計結構
M公司開發人員借助中介者模式來重新設計結構如下圖所示:
在具體實作時,為了確定系統有更好的靈活性和可擴充性,需要定義抽象中介者和抽象元件類,其中抽象元件類是所有具體元件類的公共父類,完整類圖如下圖所示:
其中,Component充當抽象同僚類,Button,List,ComboBox和TextBox充當具體同僚類,Mediator充當抽象中介者類,ConcreteMediator充當具體中介類。在ConcreteMediator中維持了對具體同僚對象的引用,為了簡化ConcreteMediator類的代碼,在其中隻定義了一個Button對象和TextBox對象。
3.2 重構後的代碼實作
(1)抽象中介者:Mediator
/// <summary>
/// 抽象中介者
/// </summary>
public abstract class Mediator
{
public abstract void ComponenetChanged(Component c);
}
(2)具體中介者:ConcreteMediator
/// <summary>
/// 具體中介者
/// </summary>
public class ConcreteMediator : Mediator
{
// 維持對各個同僚對象的引用
public Button addButton;
public List list;
public TextBox userNameTextBox;
public ComboBox cb;
// 封裝同僚對象之間的互動
public override void ComponenetChanged(Component c)
{
// 單擊按鈕
if (c == addButton)
{
Console.WriteLine("-- 單擊增加按鈕 --");
list.Update();
cb.Update();
userNameTextBox.Update();
}
// 從清單框選擇客戶
else if (c == list)
{
Console.WriteLine("-- 從清單框選擇客戶 --");
cb.Select();
userNameTextBox.SetText();
}
// 從組合框選擇客戶
else if (c == cb)
{
Console.WriteLine("-- 從組合框選擇客戶 --");
cb.Select();
userNameTextBox.SetText();
}
}
}
(3)抽象同僚類:Colleague
/// <summary>
/// 抽象同僚類:抽象元件
/// </summary>
public abstract class Component
{
protected Mediator mediator;
public void SetMediator(Mediator mediator)
{
this.mediator = mediator;
}
// 轉發調用
public void Changed()
{
mediator.ComponenetChanged(this);
}
public abstract void Update();
}
(4)具體同僚類:ConcreteColleague
/// <summary>
/// 具體同僚類:按鈕元件
/// </summary>
public class Button : Component
{
public override void Update()
{
// 按鈕不産生響應
}
}
/// <summary>
/// 具體同僚類:清單框元件
/// </summary>
public class List : Component
{
public override void Update()
{
Console.WriteLine("清單框增加一項:張無忌");
}
public void Select()
{
Console.WriteLine("清單框選中項:小龍女");
}
}
/// <summary>
/// 具體同僚類:組合框元件
/// </summary>
public class ComboBox : Component
{
public override void Update()
{
Console.WriteLine("組合框增加一項:張無忌");
}
public void Select()
{
Console.WriteLine("組合框選中項:小龍女");
}
}
/// <summary>
/// 具體同僚類:文本框元件
/// </summary>
public class TextBox : Component
{
public override void Update()
{
Console.WriteLine("客戶資訊增加成功後文本框清空");
}
public void SetText()
{
Console.WriteLine("文本框顯示:小龍女");
}
}
/// <summary>
/// 具體同僚類:标簽元件
/// </summary>
public class Label : Component
{
public override void Update()
{
Console.WriteLine("文本标簽内容改變,客戶資訊總數量加1");
}
}
(5)用戶端測試:
public class Program
{
public static void Main()
{
// Step1.定義中介者對象
ConcreteMediator mediator = new ConcreteMediator();
// Step2.定義同僚對象
Button addButton = new Button();
List list = new List();
ComboBox cb = new ComboBox();
TextBox userNameTextBox = new TextBox();
addButton.SetMediator(mediator);
list.SetMediator(mediator);
cb.SetMediator(mediator);
userNameTextBox.SetMediator(mediator);
mediator.addButton = addButton;
mediator.list = list;
mediator.cb = cb;
mediator.userNameTextBox = userNameTextBox;
// Step3.點選增加按鈕
addButton.Changed();
Console.WriteLine("---------------------------------------------");
// Step4.從清單框選擇客戶
list.Changed();
}
}
調試運作後的結果如下所示:
四、中介者模式小結
4.1 主要優點
(1)簡化了對象之間的互動,它用中介者和同僚的一對多互動替代了原來同僚之間的多對多互動。
(2)将各同僚對象解耦,可以獨立地改變和複用每個同僚和中介者,增加新的中介和同僚很友善,符合開閉原則。
(3)可以減少大量同僚子類的生成,改變同僚行為隻需要生成新的中介者子類即可。
4.2 主要缺點
具體中介者子類中包含了大量的同僚之間的互動細節,可能會導緻具體中介者類非常複雜,使得系統難以維護。
4.3 應用場景
(1)系統中對象之間存在複雜的引用關系 => 系統結構混亂且難以了解
(2)一個對象由于引用了其他很多對象并且直接和這些對象通信 => 難以複用該對象
(3)想要通過一個中間類來封裝多個類的行為又不想生成太多子類 => 引入中介者即可實作
參考資料
(1)劉偉,《設計模式的藝術—軟體開發人員内功修煉之道》
作者:周旭龍
出處:http://edisonchou.cnblogs.com
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連結。