デザインパターン ~Mediator~

設計

1. はじめに

GoFのデザインパターンにおける、Mediatorパターンについてまとめます。

2. Mediatorパターンとは

  • Mediatorという英単語は、仲介者という意味になりますが、実際には相談役と考えた方が分かりやすいです。
  • メンバーが10人いたとして、互いに指示しあっていたら、作業は大混乱します。そんなとき、立場の違う相談役がいれば、メンバーは相談役だけに報告し、メンバーへの支持は相談役だけから受けるようにすればよくなります。
  • Mediatorパターンは、相談役を通して行動を起こすようにしてもらう方式です。
  • GoFのデザインパターンでは、振る舞いに関するデザインパターンに分類されます。

3. サンプルクラス図

Mediator.PNG

4. サンプルプログラム

ログインダイアログを表示し、テキストやボタンの有効/無効状態を制御するプログラムです。

4-1. Mediatorインターフェース

相談役となるインターフェースです。

public interface Mediator {
    public abstract void createColleagues();
    public abstract void colleagueChanged();
}

4-2. LoginFrameクラス

ログインダイアログを表すクラスです。Mediatorインターフェースを実装します。

import java.awt.CheckboxGroup;
import java.awt.Color;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

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;

    public LoginFrame(String title) {
        super(title);
        setBackground(Color.lightGray);
        setLayout(new GridLayout(4, 2));

        createColleagues();
        add(checkGuest);
        add(checkLogin);
        add(new Label("Username:"));
        add(textUser);
        add(new Label("Password:"));
        add(textPass);
        add(buttonOk);
        add(buttonCancel);
        colleagueChanged();

        pack();
        setVisible(true);
    }

    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");

        checkGuest.setMediator(this);
        checkLogin.setMediator(this);
        textUser.setMediator(this);
        textPass.setMediator(this);
        buttonOk.setMediator(this);
        buttonCancel.setMediator(this);

        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();
        }
    }

    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);
    }
}

4-3. Colleagueインターフェース

メンバーとなるインターフェースです。

public interface Colleague {
    public abstract void setMediator(Mediator mediator);
    public abstract void setColleagueEnabled(boolean enabled);
}

4-4. ColleagueButtonクラス

ボタンを表すクラスです。Colleagueインターフェースを実装します。

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) {
        this.mediator = mediator;
    }

    public void setColleagueEnabled(boolean enabled) {
        // Mediatorから有効/無効が指示される
        setEnabled(enabled);
    }
}

4-5. ColleagueCheckboxクラス

チェックボックス(ここではラジオボタン)を表すクラスです。Colleagueインターフェースを実装します。

import java.awt.Checkbox;
import java.awt.CheckboxGroup;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

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) {
        this.mediator = mediator;
    }

    public void setColleagueEnabled(boolean enabled) {
        // Mediatorから有効/無効が指示される
        setEnabled(enabled);
    }

    public void itemStateChanged(ItemEvent e) {
        // 状態が変化したらMediatorに通知
        mediator.colleagueChanged();
    }
}

4-6. ColleagueTextFieldクラス

テキストボックスを表すクラスです。Colleagueインターフェースを実装します。

import java.awt.Color;
import java.awt.TextField;
import java.awt.event.TextEvent;
import java.awt.event.TextListener;

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) {
        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();
    }
}

4-7. Mainクラス

メイン処理を行うクラスです。

public class Main {
    static public void main(String args[]) {
        new LoginFrame("Mediator Sample");
    }
}

4-8. 実行結果

f.PNG

5. メリット

表示の有効/無効に関するロジックは複雑になりますが、LoginFrameクラスに仲介されています。 表示に関する仕様を変更したり、バグを見つけた場合はLoginFrameクラスのみ修正・デバッグすればよいことになります。 ロジックがColleagueButton、ColleagueCheckbox、ColleagueTextFieldに分散されいたら、書くのも、デバッグするのも、修正するのも大変になります。

6. GitHub

7. デザインパターン一覧

8. 参考

今回の記事、及びサンプルプログラムは、以下の書籍を元に作成させて頂きました。

大変分かりやすく、勉強になりました。感謝申し上げます。 デザインパターンやサンプルプログラムについての説明が詳細に書かれていますので、是非書籍の方もご覧ください。