中介者模式
在軟件工程領域,中介者模式定義了一個中介者對象,該對象封裝了系統中對象間的交互方式。 由於它可以在運行時改變程序的行為,這種模式是一種行為型模式 。
通常程序由大量的類組成,這些類中包含程序的邏輯和運算。 然而,當開發者將更多的類加入到程序中之後,類間交互關係可能變得更為複雜,這會使得代碼變得更加難以閱讀和維護,尤其是在重構的時候。 此外,程序將會變得難以修改,因為對其所做的任何修改都有可能影響到其它幾個類中的代碼。
在中介者模式中,對象間的通信過程被封裝在一個中介者(調解人)對象之中。 對象之間不再直接交互,而是通過調解人進行交互。 這麼做可以減少可交互對象間的依賴,從而降低耦合。
概述
中介者模式[1]是23個周知模式( 即GoF設計模式)中的一個,GoF設計模式旨在提供重複出現的設計問題的解決方案,以編寫靈活和可復用的面向對象軟件。也就是說,使對象更加易於實現、修改、測試和復用。
中介者設計模式可以解決什麼問題? [2]
- 避免一組相互交互的對象之間出現緊耦合。
- 能夠獨立地改變一組對象之間的交互關係而不影響其他對象。
使用直接逐個訪問並更新彼此的方式進行對象間的交互靈活性低,因為這種方式使對象彼此間緊密耦合,導致不可能單獨修改類間交互關係本身,而不影響關係中進行交互的類。並且這種方式會令對象變得無法復用,並且難以測試。
由於緊耦合的對象過多了解其他對象的內部細節,這種對象難以實現、修改、測試以及復用。
中介者模式如何解決上述問題?
- 定義一個獨立的中介者(調解員)的對象,封裝一組對象之間的交互關係。
- 對象將自己的交互委託給中介者執行,避免直接與其他對象進行交互。
對象利用中介者對象與其他對象進行間接交互,中介者對象負責控制和協調交互關係,這麼做可使得對象間鬆耦合。這些對象只訪問中介者,不了解其他對象的細節。
另見UML類圖和時序圖如下。
定義
中介者模式是為了「定義一個封裝了對象間交互關係的對象」。這種方式避免了顯式調用其他類,促進了類間的鬆耦合,並且使得類間交互關係本身可以單獨修改[3]。客戶類可以使用中介者向其他客戶類發送信息,並且通過中介者引發的事件收到信息。
結構
UML類圖和時序圖
在以上 UML 類圖中,類 Colleague1
和 Colleague2
不引用和更新彼此。
相反,它們引用一個通用的 Mediator
接口以實現類間交互的控制和協調(通過mediate()
),這使得它們獨立於類間交互的具體細節。
類 Mediator1
實現了類 Colleague1
和 Colleague2
之間的交互細節。
上述 UML 時序圖
顯示了這些類之間在運行時的交互過程:
在這個例子中,一個 Mediator1
對象
中介,即控制和協調了 Colleague1
和 Colleague2
的對象之間的交互。
如果 Colleague1
的對象需要與 Colleague2
的對象進行交互,例如更新或者同步狀態,
Colleague1
調用Mediator1
對象的 mediate(this)
方法,
以此得到對 Colleague1
對象(自己)的改動信息,並且在 Colleague2
的對象上執行 action2()
方法。
此後,
Colleague2
對象調用Mediator1
對象的 mediate(this)
方法,
得到對Colleague2
(自己)的修改的數據,並且對Colleague1
對象調用action1()
方法。
類圖
- 參與者
Mediator - 定義 Colleague 對象之間的交互接口。
ConcreteMediator -實現了Mediator接口,並且協調 Colleague對象之間的交互,知道所有的 Colleagues 對象以及其交互目的。
Colleague -定義了通過Mediator與其他Colleague對象交互的接口。
ConcreteColleague -實現了Colleague接口,並且通過它的Mediator與其他Colleague交互。
範例
C#
中介者模式確保了構件之間的鬆耦合,使得它們不直接調用彼此,而是通過中介者調用彼此。 在下面的例子中,調解人註冊了所有的構件,然後調用了它們的SetState方法。
public interface IComponent
{
void SetState(object state);
}
public class Component1 : IComponent
{
public void SetState(object state)
{
throw new NotImplementedException();
}
}
public class Component2 : IComponent
{
public void SetState(object state)
{
throw new NotImplementedException();
}
}
// Mediates the common tasks
public class Mediator
{
public IComponent Component1 { get; set; }
public IComponent Component2 { get; set; }
public void ChangeState(object state)
{
this.Component1.SetState(state);
this.Component2.SetState(state);
}
}
一個聊天室,或者一個需要客戶彼此間每次發送消息之後執行動作的系統(對聊天室來說就是每個人之間發送消息)可以使用中介者模式。但實際上對聊天室使用中介者模式只在包含遠程功能時有用。 原生的套接字不允許委託回呼函式(即通過中介者類訂閱的 MessageReceived 事件)。
public delegate void MessageReceivedEventHandler(string message, string from);
public class Mediator
{
public event MessageReceivedEventHandler MessageReceived;
public void Send(string message, string from)
{
if (MessageReceived != null)
{
Console.WriteLine("Sending '{0}' from {1}", message, from);
MessageReceived(message, from);
}
}
}
public class Person
{
private Mediator _mediator;
public string Name { get; set; }
public Person(Mediator mediator, string name)
{
Name = name;
_mediator = mediator;
_mediator.MessageReceived += new MessageReceivedEventHandler(Receive);
}
private void Receive(string message, string from)
{
if (from != Name)
Console.WriteLine("{0} received '{1}' from {2}", Name, message, from);
}
public void Send(string message)
{
_mediator.Send(message, Name);
}
}
Java
在以下示例中,一個中介者對象控制了三個互相交互的按鈕的狀態,為此它有設置狀態的三個方法:book()
, view()
和 search()
。 當相應的按鈕被激活時,對應的方法通過execute()
方法被調用。
於是這裏在交互中每個交互的參與者(本例中即按鈕)將自己的行為提交給中介者並且由中介者將這些行為轉給對應的參與者。
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
//Colleague interface
interface Command {
void execute();
}
//Abstract Mediator
interface Mediator {
void book();
void view();
void search();
void registerView(BtnView v);
void registerSearch(BtnSearch s);
void registerBook(BtnBook b);
void registerDisplay(LblDisplay d);
}
//Concrete mediator
class ParticipantMediator implements Mediator {
BtnView btnView;
BtnSearch btnSearch;
BtnBook btnBook;
LblDisplay show;
//....
public void registerView(BtnView v) {
btnView = v;
}
public void registerSearch(BtnSearch s) {
btnSearch = s;
}
public void registerBook(BtnBook b) {
btnBook = b;
}
public void registerDisplay(LblDisplay d) {
show = d;
}
public void book() {
btnBook.setEnabled(false);
btnView.setEnabled(true);
btnSearch.setEnabled(true);
show.setText("booking...");
}
public void view() {
btnView.setEnabled(false);
btnSearch.setEnabled(true);
btnBook.setEnabled(true);
show.setText("viewing...");
}
public void search() {
btnSearch.setEnabled(false);
btnView.setEnabled(true);
btnBook.setEnabled(true);
show.setText("searching...");
}
}
//A concrete colleague
class BtnView extends JButton implements Command {
Mediator med;
BtnView(ActionListener al, Mediator m) {
super("View");
addActionListener(al);
med = m;
med.registerView(this);
}
public void execute() {
med.view();
}
}
//A concrete colleague
class BtnSearch extends JButton implements Command {
Mediator med;
BtnSearch(ActionListener al, Mediator m) {
super("Search");
addActionListener(al);
med = m;
med.registerSearch(this);
}
public void execute() {
med.search();
}
}
//A concrete colleague
class BtnBook extends JButton implements Command {
Mediator med;
BtnBook(ActionListener al, Mediator m) {
super("Book");
addActionListener(al);
med = m;
med.registerBook(this);
}
public void execute() {
med.book();
}
}
class LblDisplay extends JLabel {
Mediator med;
LblDisplay(Mediator m) {
super("Just start...");
med = m;
med.registerDisplay(this);
setFont(new Font("Arial", Font.BOLD, 24));
}
}
class MediatorDemo extends JFrame implements ActionListener {
Mediator med = new ParticipantMediator();
MediatorDemo() {
JPanel p = new JPanel();
p.add(new BtnView(this, med));
p.add(new BtnBook(this, med));
p.add(new BtnSearch(this, med));
getContentPane().add(new LblDisplay(med), "North");
getContentPane().add(p, "South");
setSize(400, 200);
setVisible(true);
}
public void actionPerformed(ActionEvent ae) {
Command comd = (Command) ae.getSource();
comd.execute();
}
public static void main(String[] args) {
new MediatorDemo();
}
}
另見
- Data mediation
- 設計模式:可復用面向對象軟件的基礎, 本書引起計算機科學界對設計模式的研究
- 設計模式(計算機),解決常見軟件設計問題的標準化方法
參考文獻
- ^ Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley. 1994: 273ff. ISBN 0-201-63361-2.
- ^ The Mediator design pattern - Problem, Solution, and Applicability. w3sDesign.com. [2017-08-12]. (原始內容存檔於2018-12-16).
- ^ Gang Of Four
- ^ The Mediator design pattern - Structure and Collaboration. w3sDesign.com. [2017-08-12]. (原始內容存檔於2018-12-16).