はじめに
本エントリーは某社内で実施するデザインパターン勉強会向けの資料となります。
本エントリーで書籍「[Java言語で学ぶデザインパターン入門] (http://www.hyuki.com/dp/)」をベースに学習を進めますが、サンプルコードはC#に置き換えて解説します。
第1回:[Iteratorパターン] (http://qiita.com/Nuits/items/800f080d44b52e1cae18)
第2回:[Adapterパターン] (http://qiita.com/Nanakusajp/items/747d1913b1e8bea8d340)
第3回:[Template Methodパターン] (http://qiita.com/_kyam/items/9b5722e6a9635af6e18b)
第4回:[Factory  Methodパターン] (http://qiita.com/ayayo/items/3eece04bbc4b7504179a)
第5回:[Singletonパターン] (http://qiita.com/Keenag/items/cae53d55cb953562fee4)
第6回:[Prototypeパターン] (http://qiita.com/aconit96/items/9235d30a07f508886c7e)
第7回:Builderパターン
第8回:Abstract Factoryパターン
第9回:Bridgeパターン
第10回:Strategyパターン
第11回:Compositeパターン
第12回:Decoratorパターン
第13回:Visitorパターン
第14回:Chain of Responsibilityパターン
第15回:Facadeパターン
Mediatorパターンとは
Mediatorとは、仲介者を意味する英単語です。
このパターンでは、複雑に絡み合った複数のオブジェクト間の関係を必ず仲介者を介して処理を行うようにすることで、通信を行うオブジェクト間の結合度を下げることができます。
ここではログインフォームの入力制御を実行するプログラムを例に紹介します。
サンプルプログラムの各クラスの役割
| 名前 | 役割 | 
|---|---|
| IMediator | Colleagueと通信を行い、調整を行うためのインターフェース | 
| IColleague | 他のColleagueと通信するために、Mediatorと通信を行うインターフェース | 
| ColleagueButton | IColleagueを実装し、LoginFormに通信を行うボタンコントロール | 
| ColleagueRadioButton | IColleagueを実装し、LoginFormに通信を行うラジオボタンコントロール | 
| ColleagueTextbox | IColleagueを実装し、LoginFormに通信を行うテキストボックスコントロール | 
| LoginForm | IMediatorを実装したフォーム。IColleagueを実装したユーザコントロールクラスとの通信を行う | 
| Program | 動作用のクラス | 
IMediatorインターフェース
IMediatorインターフェースにはIColleagueを実装した各メンバーを管理するために必要なメソッドが定義されています。
CreateColleaguesメソッドは、Mediatorが管理するオブジェクトを生成します。
ColleagueChangedメソッドは、各オブジェクトから呼び出されるメソッドです。
namespace MediatorPattern
{
    public interface IMediator
    {
        void CreateColleagues();
        void ColleagueChanged();
    }
}
IColleagueインターフェース
SetMediatorメソッドは、Mediatorによって最初に呼び出されるメソッドで、後々Mediatorを呼び出すためのインスタンスを保持します。
SetColleagueEnabledメソッドは、Mediatorからの指示を受けて実行されます。ここではユーザコントロールを活性/非活性にする処理を実装します。
namespace MediatorPattern
{
    public interface IColleague
    {
        void SetMediator(IMediator mediator);
        void SetColleagueEnabled(bool enabled);
    }
}
Colleagueクラス
以下、IColleagueを実装した各コントロールになります。
 ・ColleagueButton
 ・ColleagueRadioButton
 ・ColleagueTextbox
上記コントロールはそれぞれ、MediatorによってSetMediatorメソッドを呼び出され、Mediatorクラスのインスタンスを保持します。
また、コントロールの状態が変化した時に、その変化した旨をSetColleagueEnabledメソッドによりMediatorに通知します。
ColleagueButtonクラス
namespace MediatorPattern
{
    public partial class ColleagueButton : UserControl, IColleague
    {
        private IMediator _mediator;
        public ColleagueButton()
        {
        }
        public void SetMediator(IMediator mediator)
        {
            _mediator = mediator;
        }
        public void SetColleagueEnabled(bool enabled)
        {
            Enabled = enabled;
        }
    }
}
ColleagueRadioButtonクラス
  namespace MediatorPattern
{
    public partial class ColleagueRadioButton : UserControl,IColleague
    {
        private IMediator _mediator;
        public ColleagueRadioButton()
        {
        }
        public void SetMediator(IMediator mediator)
        {
            _mediator = mediator;
        }
        public void SetColleagueEnabled(bool enabled)
        {
            Enabled = enabled;
        }
        protected override void OnCheckedChanged(EventArgs e) {
            base.OnCheckedChanged(e);
            if (mediator != null)
                mediator.ColleagueChanged();
        }
    }
}
ColleagueTextboxクラス
namespace MediatorPattern
{
    public partial class ColleagueTextbox : UserControl,IColleague
    {
        private IMediator _mediator;
        public ColleagueTextbox()
        {
        }
        public void SetMediator(IMediator mediator)
        {
            _mediator = mediator;
        }
        public void SetColleagueEnabled(bool enabled)
        {
            Enabled = enabled;
        }
        // 文字列が変化したらMediatorに通知
        protected override void OnTextChanged(EventArgs e)
        {
            base.OnTextChanged(e);
            if (mediator != null)
                mediator.ColleagueChanged();
        }
    }
}
LoginFormクラス
Mediatorを実装したLoginFormクラスです。
各Colleagueクラスの状態を管理する制御を行います。
ColleagueChangedメソッドでGuestかLoginかを判定し、各コントロールの状態を指定します。
Loginの場合、UserPassChangedメソッドでさらに各コントロールの状態を指示しています。
namespace MediatorPattern
{
    public partial class LoginForm : Form,IMediator
    {
        public LoginForm()
        {
            InitializeComponent();
            CreateColleagues();
            ColleagueChanged();
        }
        public virtual void CreateColleagues()
        {
            checkGuest.SetMediator(this);
            checkLogin.SetMediator(this);
            textUser.SetMediator(this);
            textPass.SetMediator(this);
            buttonOk.SetMediator(this);
            buttonCancel.SetMediator(this);
        }
        public virtual void ColleagueChanged()
        {
            if (checkGuest.Checked)
            {
                // Guest 
                textUser.SetColleagueEnabled(false);
                textPass.SetColleagueEnabled(false);
                buttonOk.SetColleagueEnabled(true);
            }
            else
            {
                // Login 
                textUser.SetColleagueEnabled(true);
                UserPassChanged();
            }
        }
        // textUserまたはtextPassの変更があった。
        // 各Colleageの有効/無効を判定する。
        private void UserPassChanged()
        {
            if (textUser.Text.Length > 0)
            {
                textPass.SetColleagueEnabled(true);
                if (textPass.Text.Length > 0)
                {
                    buttonOk.SetColleagueEnabled(true);
                }
                else
                {
                    buttonOk.SetColleagueEnabled(false);
                }
            }
            else
            {
                textPass.SetColleagueEnabled(false);
                buttonOk.SetColleagueEnabled(false);
            }
        }
        private void buttonOk_Click(object sender, EventArgs e)
        {
            Console.WriteLine(sender + " : " + e.ToString());
            Close();
        }
    }
}
Program
LoginFormを起動します。
namespace MediatorPattern
{
    static class Program
    {
        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new LoginForm());
        }
    }
}
実行結果
Mediatorパターンのメリット
このデザインパターンを使用するメリットは以下の通りです。
 ・複雑に絡み合うオブジェクトの直接の通信を止め、Mediator役のクラスを仲介することで通信をシンプルにすること
 ・Colleague役クラスの改修・追加の際、影響範囲を最小に留める

