GoFのデザインパターンをPythonで学習してみたいと思います。
■ Mediator(メディエーター・パターン)
「Mediator」という英単語は、「仲介者」を意味します。
このパターンは、複雑に絡み合った複数のオブジェクト間の関係を、必ず「仲介者」を介して処理を行う様にすることで単純かつ明快なインタフェースを提供するパターンです。つまり、「Mediator」パターンとは、管轄下にある複数のオブジェクト各々からの問い合わせを受け、適宜判断を行い、管轄下にあるオブジェクト全体、または一部へ指示を出す「仲介人」の役割を果たすクラスを利用するパターンです。
UML class and sequence diagram
UML class diagram
(以上、「ITエンジニアのための技術支援サイト by IT専科」より引用)
■ "Mediator"のサンプルプログラム
実際に、Mediatorパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。ここでは、"なんちゃって、ユーザ認証画面"を想像してください。
- ユーザ名"hoge", パスワード"fuga"のユーザが存在するとする
- ユーザ名とパスワードが入力されると、**"login button"**がアクティブになる
- **"login button"**をクリックして、ユーザ認証が成功したか、どうか判定する
なお、サンプルプログラムでは、第一引数:ユーザ名、第二引数:パスワードを指定することにより、"なんちゃって、ユーザ認証画面"に、ユーザ名とパスワードを入力したものとします。
(事例1) 誤ったパスワードを指定して、ユーザ認証が失敗する
ユーザ名は正しく入力されたので、**"login button"**が有効になりましたが、ユーザ認証は失敗しました。
$ python Main.py hoge huga
(Active login button)
(ID/PW is incorrect)
Login Failed!!
(事例2) パスワードが未指定だったので、ユーザ認証が失敗する
**"login button"**が有効ならずに、ユーザ認証は失敗しました。
$ python Main.py hoge
Login Failed!!
(事例3) 正しいユーザ名, パスワードを指定して、ユーザ認証が成功する
ユーザ名は正しく入力されたので、**"login button"**が有効になり、ユーザ認証も成功しました。
$ python Main.py hoge fuga
(Active login button)
(ID/PW is confirmed)
Login Succeed!!
以上で、想定どおり、サンプリプログラムが動作しました。
■ サンプルプログラムの詳細
Gitリポジトリにも、同様のコードをアップしています。
https://github.com/ttsubo/study_of_design_pattern/tree/master/Mediator
- ディレクトリ構成
.
├── Main.py
└── mediator
├── __init__.py
├── colleague.py
└── mediator.py
(1) Mediator(調停者、仲介者)の役
Mediator
役は、Colleague
役と通信を行なって、調整を行うためのインタフェースを定めます。
サンプルプログラムでは、Mediator
クラスが、この役を努めます。
from abc import ABCMeta, abstractmethod
class Mediator(metaclass=ABCMeta):
@abstractmethod
def on_change(self, component):
pass
(2) ConcreteMediator(具体的な調停者、仲介者)の役
ConcreteMediator
役は、Mediator
役のインタフェースを実装し、実際の調整を行います。
サンプルプログラムでは、ConcreteMediator
クラスが、この役を努めます。
class ConcreteMediator(Mediator):
def __init__(self):
self.authentication = False
def setColleagues(self, inputIdObj, inputPwObj, buttonObj):
self.inputIdObj = inputIdObj
self.inputPwObj = inputPwObj
self.buttonObj = buttonObj
def on_change(self, component):
if component.name == "ID" or component.name == "PW":
self.__refreshButton()
elif component.name == "Login":
self.__authentication()
def __refreshButton(self):
if self.inputIdObj.text is not None and self.inputPwObj.text is not None:
print("(Active login button)")
self.buttonObj.active = True
def __authentication(self):
if self.inputIdObj.text == "hoge" and self.inputPwObj.text == "fuga":
print("(ID/PW is confirmed)")
self.authentication = True
else:
print("(ID/PW is incorrect)")
(3) Colleague(同僚)の役
Colleague
役は、Mediator
役と通信を行うインタフェースとを紐付けます。
サンプルプログラムでは、Colleague
クラスが、この役を努めます。
from mediator.mediator import Mediator
class Colleague(Mediator):
def __init__(self, mediatorObj, name):
self.mediator = mediatorObj
self.name = name
def on_change(self):
if self.mediator is not None:
self.mediator.on_change(self)
(4) ConcreteColleague(具体的な同僚)の役
ConcreteColleague
役は、Colleague
役と紐付けます。
サンプルプログラムでは、ConcreteColleagueButton
クラスと、ConcreteColleagueTextArea
クラスが、この役を努めます。
class ConcreteColleagueButton(Colleague):
def __init__(self, mediatorObj, name=None):
super(ConcreteColleagueButton, self).__init__(mediatorObj, name)
self.active = False
def clickButton(self):
if self.active:
self.on_change()
return self.mediator.authentication
def checkButtonStatus(self):
return self.active
class ConcreteColleagueTextArea(Colleague):
def __init__(self, mediatorObj, name=None):
super(ConcreteColleagueTextArea, self).__init__(mediatorObj, name)
self.text = None
def inputText(self, text):
self.text = text
self.on_change()
(5) Client(依頼人)の役
サンプルプログラムでは、startMain
メソッドが、この役を努めます。
import sys
from mediator.mediator import ConcreteMediator
from mediator.colleague import ConcreteColleagueTextArea, ConcreteColleagueButton
def startMain(userid, password):
m = ConcreteMediator()
inputIdObj = ConcreteColleagueTextArea(m, "ID")
inputPwObj = ConcreteColleagueTextArea(m, "PW")
pushButtonObj = ConcreteColleagueButton(m, "Login")
m.setColleagues(inputIdObj, inputPwObj, pushButtonObj)
inputIdObj.inputText(userid)
inputPwObj.inputText(password)
if pushButtonObj.clickButton():
print("Login Succeed!!")
else:
print("Login Failed!!")
def check_input_data(params):
if len(params)==3:
userid = params[1]
password = params[2]
elif len(params)==2:
userid = params[1]
password = None
elif len(params)==1:
userid = None
password = None
return userid, password
if __name__ == "__main__":
userid, password = check_input_data(sys.argv)
startMain(userid, password)