Help us understand the problem. What is going on with this article?

Pythonで、デザインパターン「Mediator」を学ぶ

GoFのデザインパターンをPythonで学習してみたいと思います。

■ Mediator(メディエーター・パターン)

「Mediator」という英単語は、「仲介者」を意味します。
このパターンは、複雑に絡み合った複数のオブジェクト間の関係を、必ず「仲介者」を介して処理を行う様にすることで単純かつ明快なインタフェースを提供するパターンです。つまり、「Mediator」パターンとは、管轄下にある複数のオブジェクト各々からの問い合わせを受け、適宜判断を行い、管轄下にあるオブジェクト全体、または一部へ指示を出す「仲介人」の役割を果たすクラスを利用するパターンです。

UML class and sequence diagram

W3sDesign_Mediator_Design_Pattern_UML.jpg

UML class diagram

designpattern-mediator01.gif
(以上、「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クラスが、この役を努めます。

mediator/mediator.py
from abc import ABCMeta, abstractmethod

class Mediator(metaclass=ABCMeta):
    @abstractmethod
    def on_change(self, component):
        pass

(2) ConcreteMediator(具体的な調停者、仲介者)の役

ConcreteMediator役は、Mediator役のインタフェースを実装し、実際の調整を行います。
サンプルプログラムでは、ConcreteMediatorクラスが、この役を努めます。

mediator/mediator.py
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クラスが、この役を努めます。

mediator/colleague.py
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クラスが、この役を努めます。

mediator/colleague.py
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メソッドが、この役を努めます。

Main.py
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)

■ 参考URL

ttsubo
SDN分野に興味があります。 ただいま、golangを勉強中です。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away