---
# System prepended metadata

title: 中介者模式 Mediator
tags: [Behavioral Patterns]

---

---
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=閔致]