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