【Java】Mediator パターン【デザインパターン】
目次
Mediatorパターンについて
Mediatorパターンとは
- Mediatorパターンは、相談役を通して行動を起こすようにしてもらうパターンです。
- Mediatorという英単語は"相談役"と考えると分かりやすいです。
- メンバーが、互いに指示しあっていたら、作業は混乱します。
- そんなとき、立場の違う相談役を立て、相談役だけから、メンバーへの指示を行うようにします
サンプルプログラム
- Mediator パターンを使って、ログインダイアログを表示し、名前やパスワードを入力するテキストフィールドや、ボタンの有効/無効状態を、制御する例を取り上げます
- これらのテキストフィールドやボタンのロジックを各クラスに分散させると、それぞれのオブジェクトは関係し合っているので、お互いをコントロールし合うことになり、大変なことになります
- このように、多数のオブジェクト間の調整を行う必要があるときに、Mediatoパターンを使います
- 個々のオブジェクトが互いに通信するのではなく、"相談役"のみと通信し、"相談役"のみに、表示のロジックを記述します
- 各クラスの役割は以下のようになっています
名前 | 役割 |
---|---|
Mediator | 相談役のインターフェースを定めるインターフェース |
Colleague | メンバのインターフェースを定めるインターフェース |
ColleagueButton(ConcreteColleague) | Colleagueインターフェースを実装し、ボタンを表すクラス |
ColleagueCheckbox(ConcreteColleague) | Colleagueインターフェースを実装し、ラジオボタンを表すクラス |
ColleagueTextField (ConcreteColleague) | Colleagueインターフェースを実装し、テキスト入力を表すクラス |
LoginFrame(ConcreteMediator) | Mediatorインターフェースを実装し、ログインダイアログを表すクラス |
Mediatorインターフェース
- 相談役を表現するインターフェースです
- 具体的な相談役(LoginFrame)は、このインターフェースを実装します
- createColleaguesメソッドで、メンバ(必要なボタンやテキストフィールド)を生成します
- colleagueChangedメソッドは、メンバから呼ばれるメソッドです。相談役への"相談"に当たります。
public interface Mediator { public abstract void createColleagues(); //メンバを生成 public abstract void colleagueChanged(); //相談役への"相談" }
Colleagueインターフェース
- 相談役に相談を持ちかけるメンバを表すインターフェースです
- 具体的なメンバはこのクラスを実装します
- setMediatorメソッドは、Mediatorインターフェースを実装したLoginFrameが最初に呼ぶメソッド
- setColleagueEnabledメソッドは、相談役からの指示に当たります
- 自分自身を有効/無効に設定するのは、自分ではなく、相談役の判断で決めます
public interface Colleague { public abstract void setMediator(Mediator mediator); //相談役を設定 public abstract void setColleagueEnabled(boolean enabled); //相談役からの指示 }
ColleagueButtonクラス
*ボタンを表すクラスです。Colleagueインターフェースを実装し、LoginFrameクラスと協調動作を行います * setEnabledがtrueなら、ボタンが押せる状態です
import java.awt.Button; public class ColleagueButton extends Button implements Colleague { private Mediator mediator; public ColleagueButton(String caption) { super(caption); } public void setMediator(Mediator mediator) { // Mediatorを保持 this.mediator = mediator; } public void setColleagueEnabled(boolean enabled) { // Mediatorから有効/無効が指示される setEnabled(enabled); } }
ColleagueCheckboxクラス
- チェックボックス(ここではラジオボタン)を表すクラスです。Colleagueインターフェースを実装します。
java.awt.event.ItemListener
を実装し、ボタンの状態の変化を、itemStateChangedメソッドでキャッチします
import java.awt.Checkbox; import java.awt.CheckboxGroup; import java.awt.event.ItemListener; import java.awt.event.ItemEvent; public class ColleagueCheckbox extends Checkbox implements ItemListener, Colleague { private Mediator mediator; public ColleagueCheckbox(String caption, CheckboxGroup group, boolean state) { // コンストラクタ super(caption, group, state); } public void setMediator(Mediator mediator) { // Mediatorを保持 this.mediator = mediator; } public void setColleagueEnabled(boolean enabled) { // Mediatorから有効/無効が指示される setEnabled(enabled); } public void itemStateChanged(ItemEvent e) { // 状態が変化したらMediatorに通知 mediator.colleagueChanged(); } }
ColleagueTextFieldクラス
- テキストボックスを表すクラスです。Colleagueインターフェースを実装します。
java.awt.event.TextListener
を実装し、textValueChangedメソッドでテキストの内容の変化をキャッチします- テキストの内容に変更があった場合に、AWTのフレームワーク側から呼び出されます
- colleagueChangedメソッドを呼び出して、自分の文字列内容が変化したことを報告しています
import java.awt.TextField; import java.awt.Color; import java.awt.event.TextListener; import java.awt.event.TextEvent; public class ColleagueTextField extends TextField implements TextListener, Colleague { private Mediator mediator; public ColleagueTextField(String text, int columns) { //コンストラクタ super(text, columns); } public void setMediator(Mediator mediator) { // Mediator保持 this.mediator = mediator; } public void setColleagueEnabled(boolean enabled) { // Mediatorから有効、無効が指示される setEnabled(enabled); setBackground(enabled ? Color.white : Color.lightGray); } public void textValueChanged(TextEvent e) { // 文字列が変化したらMediatorに追加 mediator.colleagueChanged(); } }
LoginFrameクラス
- ログインダイアログを表すクラスです。Mediatorインターフェースを実装します
- GUIIアプリケーションを作るためのクラスである
java.awt.Frame
のサブクラスです - コンストラクタの中で、背景色・レイアウトマネージャ設定・Colleagueの生成・配置・初期状態の設定・表示を行います
- colleagueChangedメソッドで、各Colleageの有効/無効を判定します
- 相談役への相談が、colleagueChangedメソッドに集約しています
import java.awt.Frame; import java.awt.Label; import java.awt.Color; import java.awt.CheckboxGroup; import java.awt.GridLayout; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; public class LoginFrame extends Frame implements ActionListener, Mediator { private ColleagueCheckbox checkGuest; private ColleagueCheckbox checkLogin; private ColleagueTextField textUser; private ColleagueTextField textPass; private ColleagueButton buttonOk; private ColleagueButton buttonCancel; // コンストラクタ。 // Colleagueたちを生成し、配置した後に表示を行う。 public LoginFrame(String title) { super(title); setBackground(Color.lightGray); // レイアウトマネージャを使って4×2のグリッドを作る setLayout(new GridLayout(4, 2)); // Colleagueたちの生成 createColleagues(); // 配置 add(checkGuest); add(checkLogin); add(new Label("Username:")); add(textUser); add(new Label("Password:")); add(textPass); add(buttonOk); add(buttonCancel); // 有効/無効の初期指定 colleagueChanged(); // 表示 pack(); show(); } // Colleagueたちを生成する。 public void createColleagues() { // 生成 CheckboxGroup g = new CheckboxGroup(); checkGuest = new ColleagueCheckbox("Guest", g, true); checkLogin = new ColleagueCheckbox("Login", g, false); textUser = new ColleagueTextField("", 10); textPass = new ColleagueTextField("", 10); textPass.setEchoChar('*'); buttonOk = new ColleagueButton("OK"); buttonCancel = new ColleagueButton("Cancel"); // Mediatorのセット checkGuest.setMediator(this); checkLogin.setMediator(this); textUser.setMediator(this); textPass.setMediator(this); buttonOk.setMediator(this); buttonCancel.setMediator(this); // Listenerのセット checkGuest.addItemListener(checkGuest); checkLogin.addItemListener(checkLogin); textUser.addTextListener(textUser); textPass.addTextListener(textPass); buttonOk.addActionListener(this); buttonCancel.addActionListener(this); } // Colleageからの通知で各Colleageの有効/無効を判定する。 public void colleagueChanged() { if (checkGuest.getState()) { // Guest mode textUser.setColleagueEnabled(false); textPass.setColleagueEnabled(false); buttonOk.setColleagueEnabled(true); } else { // Login mode textUser.setColleagueEnabled(true); userpassChanged(); } } // textUserまたはtextPassの変更があった。 // 各Colleageの有効/無効を判定する。 private void userpassChanged() { if (textUser.getText().length() > 0) { textPass.setColleagueEnabled(true); if (textPass.getText().length() > 0) { buttonOk.setColleagueEnabled(true); } else { buttonOk.setColleagueEnabled(false); } } else { textPass.setColleagueEnabled(false); buttonOk.setColleagueEnabled(false); } } public void actionPerformed(ActionEvent e) { System.out.println(e.toString()); System.exit(0); } }
Mainクラスで動作確認
import java.awt.*; import java.awt.event.*; public class Main { static public void main(String args[]) { new LoginFrame("Mediator Sample"); } }
Mediatorパターンのメリット
- 表示の有効/無効に関するロジックは複雑になりますが、LoginFrameクラスに集約されているので、改修する場合はLoginFrameクラスのみ修正すればよいことになります
- 通信経路を少なくすることで、インスタンスが多くなっても、シンプルな設計にできます
- アプリケーションへの依存性が高い部分は、LoginFrameクラスに集約されているので、LoginFrameクラスの再利用性は低いです
今日のポイント
- Mediatorパターンは、相談役を通して行動を起こすようにしてもらうパターンです。
- 関係し合う、多数のオブジェクト間の調整を行う必要があるときに、Mediatoパターンを使います
- 表示の有効/無効に関するロジックは複雑になりますが、LoginFrameクラスに集約されているので、改修する場合はLoginFrameクラスのみ修正すればよいことになります
- アプリケーションへの依存性が高い部分は、LoginFrameクラスに集約されているので、LoginFrameクラスの再利用性は低いです
本日もお疲れ様です😊