---
tags: Behavioral Patterns
---
# 中介者模式 Mediator
Mediator統一管理物件,物件之間禁止互相溝通(降低耦合)。
也就說Mediator可以理解成一個唯一的溝通元件,只要要和其他元件做什麼事都要透過它。
通常Mediator也能符合**封裝與迪米特法則(Law of Demete)**
## 角色
**Mediator 參與者**
Mediator 是跟 Colleague 進行溝通,規定調整的介面。例如 Mediator 介面。
**ConcreteMediator 參與者**
ConcreteMediator 是實作 Mediator 的介面,進行實際的調整。例如 LoginFrame 類別。
**Colleague 參與者**
Colleague 是規定與 Mediator 溝通的介面。例如 Colleague 介面。
**ConcreteColleague 參與者**
ConcreteColleague 實作 Colleague 的介面。例如 ColleagueButton、ColleagueTextField 及 ColleagueCheckbox 等幾個類別。
## 如何寫
Mediator先開出功能以及可以控制的變數,然後實作成ConcreteMediator負責撰寫實際的功能。
通常會在main新增一個ConcreteMediator,把要控制的變數放入,這樣就完成了協調class的基本設定。
介面的Colleague至少會有一個setMediator的method。
實作成的ConcreteColleague,會有各自的method,例如登入失敗的method,這個method下就會去呼叫設定進來的ConcreteMediator的fail method,fail的method可能就會去設定其他的class去做不同的動作,如:計算登入失敗次數、顯示文字、清空欄位。
new出一個ConcreteColleague時,都要設定正確的ConcreteMediator。
## Mediator跟Facade差異
Mediator主要用一次協調多個class上,像是一個showPasswordError的method,就要更改文字的class元件、重設input的欄位等。
跟facade有什麼不同,我認為facade是撰寫一個管理很多class的method,例如一個叫做開機的method,使用者不知道這個method底下還有什麼method,而事實上開機的method其實還有呼叫很多其他**class**的method。
而Mediator雖然乍看之下差不多,但是我覺得Mediator主要是與其他class去做溝通。
**Facade的核心在於它只是去執行各種class底下的method,而Mediator是負責撰寫一系列的其他class的method的工作**
**以更改舉例,Facade要跑去其他的class更改,而Mediator只要在自己的class更改。**
## 範例
情境:
假設你現在要設計一個 GUI 的登入畫面,有四個元件:可以讓使用者輸入帳號密碼的 EditText,顯示帳號或是密碼有問題的 TextView,以及可以讓使用者確定要登入的 Button。一般來說,當帳號密碼有問題,登入 Button 就不能按,且 TextView 可能需要顯示提示訊息。當沒有使用中介者模式時,這些元件可能彼此都要互相知道才能達到上述需求,但加入了中介者模式後:
```java
//定義Colleague
public abstract class View {
protected mediator mMediator;
protected void setMediator(Mediator m){
mMediator = m;
}
public abstract void action();
}
//ConcreateCooleague
public class TextView extends View {
@Override
public void action(){
System.out.println("User touch TextView");
}
}
public abstract class EditText extends View {
@Override
public void action(){
System.out.println("User touch EditText");
}
}
public class AccountEditText extends EditText {
// 假設 EditText 有 text 改變的 callback
@Override
public void onTextChanged()
{
if(!isAccountValid())
{
mMediator.showAccountError();
}
}
public boolean isAccountValid()
{
// 省略
}
}
public class PasswordEditText extends EditText {
// 假設 EditText 有 text 改變的 callback
@Override
public void onTextChanged()
{
if(!isPasswordValid())
{
mMediator.showPasswordError();
}
}
public boolean isPasswordValid()
{
// 省略
}
}
public class Button extends View {
@Override
public void action()
{
if(loginSuccess())
{
mMediator.loginSuccess();
}
else
{
mMediator.loginFail();
}
}
}
// Mediator 介面
public interface Mediator {
void loginFail();
void loginSuccess();
void showAccountError();
void showPasswordError();
}
// 實際的 Mediator 類別
// 負責處理使用者登入的不同情況的畫面顯示
public class LoginMediator implements Mediator {
private TextView mHintView;
private EditText mAccountView;
private EditText mPasswordView;
private Button mSubmitButton;
public LoginMediator(TextView hintView, EditText accountView,
EditText passwordView, Button submitButton)
{
mHintView = hintView;
mAccountView = accountView;
mPasswordView = passwordView;
mSubmitButton = submitButton;
}
@Override
public void loginFail()
{
// 登入失敗時, 提示使用者,
// 並把帳號密碼清空
mHintView.setText("Login failed");
mSubmitButton.setEnabled(true);
mAccountView.setText("");
mPasswordView.setText("");
}
@Override
public void loginFail()
{
// 可能跳到其他畫面,
// 這個畫面可以不做事
mHintView.setText("Login success");
}
@Override
public void showAccountError()
{
// 帳號有問題, 提示使用者,
// 並讓登入按鈕不能按
mHintView.setText("User account error");
mSubmitButton.setEnabled(false);
mAccountView.setText("");
}
@Override
public void showPasswordError()
{
// 密碼有問題, 提示使用者,
// 並讓登入按鈕不能按
mHintView.setText("User account error");
mSubmitButton.setEnabled(false);
mPasswordView.setText("");
}
}
public class MediatorDemo {
Mediator mMediator;
public static void main(String[] args)
{
// 實例化所需 GUI 元件, 這邊參考就好
TextView hintView = new TextView(...);
EditText accountView = new AccountEditText(...);
EditText passwordView = new PasswordEditText(...);
Button submitView = new Button(...);
mMediator = new LoginMediator(hintView, accountView,
passwordView, submitView);
hintView.setMediator(mMediator);
accountView.setMediator(mMediator);
passwordView.setMediator(mMediator);
submitView.setMediator(mMediator);
// 等待 GUI 事件...
}
// 假設我們有設定元件 touch 的 callback
@Override
public void onViewClicked(View view)
{
view.action();
}
}
```
>[name=閔致]