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

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

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

## UML class and sequence diagram
![W3sDesign_Mediator_Design_Pattern_UML.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/01c6c40b-fff7-eb3b-2680-82a10aaf672e.jpeg)
## UML class diagram
![designpattern-mediator01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/87aaa2b4-ea46-97b7-d8ad-3f59c2d46d37.gif)
[(以上、「ITエンジニアのための技術支援サイト by IT専科」より引用)](http://www.itsenka.com/contents/development/designpattern/mediator.html)

# ■ "Mediator"のサンプルプログラム
実際に、Mediatorパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。ここでは、<font color="blue">"なんちゃって、ユーザ認証画面"</font>を想像してください。

- ユーザ名"hoge", パスワード"fuga"のユーザが存在するとする
- ユーザ名とパスワードが入力されると、**"login button"**がアクティブになる
- **"login button"**をクリックして、ユーザ認証が成功したか、どうか判定する


なお、サンプルプログラムでは、第一引数：ユーザ名、第二引数：パスワードを指定することにより、<font color="blue">"なんちゃって、ユーザ認証画面"</font>に、ユーザ名とパスワードを入力したものとします。

## (事例1) 誤ったパスワードを指定して、ユーザ認証が失敗する
ユーザ名は正しく入力されたので、**"login button"**が有効になりましたが、ユーザ認証は失敗しました。

```console
$ python Main.py hoge huga
(Active login button)
(ID/PW is incorrect)
Login Failed!!
```

## (事例2) パスワードが未指定だったので、ユーザ認証が失敗する
**"login button"**が有効ならずに、ユーザ認証は失敗しました。

```console
$ python Main.py hoge
Login Failed!!
```

## (事例3) 正しいユーザ名, パスワードを指定して、ユーザ認証が成功する
ユーザ名は正しく入力されたので、**"login button"**が有効になり、ユーザ認証も成功しました。

```console
$ 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

- ディレクトリ構成

```console
.
├── Main.py
└── mediator
    ├── __init__.py
    ├── colleague.py
    └── mediator.py
```
## (1) Mediator（調停者、仲介者）の役
`Mediator`役は、`Colleague`役と通信を行なって、調整を行うためのインタフェースを定めます。
サンプルプログラムでは、`Mediator`クラスが、この役を努めます。

```python:mediator/mediator.py
from abc import ABCMeta, abstractmethod

class Mediator(metaclass=ABCMeta):
    @abstractmethod
    def on_change(self, component):
        pass
```
## (2) ConcreteMediator（具体的な調停者、仲介者）の役
`ConcreteMediator`役は、`Mediator`役のインタフェースを実装し、実際の調整を行います。
サンプルプログラムでは、`ConcreteMediator`クラスが、この役を努めます。

```python: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`クラスが、この役を努めます。

```python: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`クラスが、この役を努めます。

```python: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`メソッドが、この役を努めます。

```python: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
- [Pythonにおけるデザインパターン ~Pyデザパタ~ / Mediatorパターン](https://pydp.info/GoF_dp/behavior/20_State/)
- [Mediator Design Pattern In Python](http://coursegalaxy.com/designpatterns/mediator/mediator_python1.htm)