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

GoFのデザインパターンを学習する素材として、書籍「[増補改訂版Java言語で学ぶデザインパターン入門](https://www.hyuki.com/dp/)」が参考になるみたいですね。ただ、取り上げられている実例は、JAVAベースのため、自分の理解を深めるためにも、Pythonで同等のプラクティスに挑んでみました。

# ■ Facade（ファサード・パターン）
Facadeパターンあるいは Façadeパターン（ファサード・パターン）とは、GoF（Gang of Four; 4人のギャングたち）によって定義された、コンピュータソフトウェアのデザインパターンの1つである。Facade（ファサード）とは「建物の正面」を意味する。異なるサブシステムを単純な操作だけを持ったFacadeクラスで結び、サブシステム間の独立性を高める事を目的とする。

## UML class and sequence diagram
![W3sDesign_Facade_Design_Pattern_UML.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/ee3e77d2-4cf8-c88f-f2f4-d4d0b1e30cbb.jpeg)
## UML class diagram
![Example_of_Facade_design_pattern_in_UML.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/7abb8840-fbd6-dfcc-ff09-a8a3f087599e.png)
[(以上、ウィキペディア（Wikipedia）より引用)](https://ja.wikipedia.org/wiki/Facade_パターン)

## □ 備忘録
Facadeパターンは、複雑に絡み合ってごちゃごちゃした詳細をまとめ、高レベルのインタフェース（API）を提供するそうです。Facade役は、システムの外側に対してはシンプルなインタフェースを見せるそうです。また、Facade役はシステム内側にある各クラスの役割を考えて、正しい順番でクラスを利用するそうです。

そういえば、昔のOpenStack Heat（オーケストレーション）の内部構造を調査していたときに、まさに、`Facade`パターンに遭遇したのを思い出しました。
確か、 OpenStack heat-engine側では、SQLAlchemy経由でデータベースを利用していて、そのデータベース用セッションを、`Facade`パターンで管理していたはずです。

```python:heat/heat/db/sqlalchemy/api.py
def get_facade():
    global _facade

    if not _facade:
        _facade = db_session.EngineFacade.from_config(CONF)
        if CONF.profiler.enabled:
            if CONF.profiler.trace_sqlalchemy:
                osprofiler.sqlalchemy.add_tracing(sqlalchemy,
                                                  _facade.get_engine(),
                                                  "db")

    return _facade
```
https://github.com/openstack/heat/blob/mitaka-eol/heat/db/sqlalchemy/api.py#L50-L61


# ■ "Facade"のサンプルプログラム
実際に、Facadeパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。

```console
$ python Main.py 
welcome1.html is created for hyuki@hyuki.com (Hiroshi Yuki)
welcome2.html is created for mamoru@hyuki.com (Mamoru Takahashi)
```
サンプルプログラムを起動すると、２つのhtmlファイルが生成されます。
おのおのを、Webブラウザを確認してみるとこんな感じになりました。

- welcome1.html

|![page1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/92bc120d-a5ea-593a-1592-f70d92d5234c.png)|
|:-:|
- welcome2.html

|![page2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/f1dd9dbf-1955-966d-9411-765e1738e9f6.png)|
|:-:|

# ■ サンプルプログラムの詳細
Gitリポジトリにも、同様のコードをアップしています。
https://github.com/ttsubo/study_of_design_pattern/tree/master/Facade

- ディレクトリ構成

```console
.
├── Main.py
├── maildata.ini
└── pagemaker
    ├── __init__.py
    ├── database.py
    ├── html_writer.py
    └── page_maker.py
```
## (1) Facade（正面）の役
`Facade`役は、システムを構成しているその他大勢の役の「シンプルな窓口」となります。`Facade`役は、高レベルでシンプルなインタフェースをシステム外部に提供します。
サンプルプログラムでは、`PageMaker`クラスが、この役を努めます。

```python:pagemaker/page_maker.py
import sys
import json
from pagemaker.database import Database
from pagemaker.html_writer import HtmlWriter

class PageMaker(object):
    @classmethod
    def makeWelcomePage(cls, mailaddr, filename):
        try:
            prop = Database.getProperties('maildata')
            username =prop[mailaddr]
            writer = HtmlWriter(open(filename, 'w'))
            writer.title('Welcom to {}s page!'.format(username))
            writer.paragraph("We'll wait for your sending")
            writer.mailto(mailaddr, username)
            writer.close()
            print('{} is created for {} ({})'.format(filename, mailaddr, username))
        except Exception:
            print("# Failure occurred")
```
## (2) システムを構成しているその他大勢の役
その他大勢の役は、それぞれの仕事を行いますが、`Facade`役のことは意識しません。`Facade`役から呼び出されて仕事を行いますが、その他大勢の役の方から`Facade`役を呼び出すことはありません。
サンプルプログラムでは、`Database`クラスと、`HtmlWriter`クラスが、この役を努めます。

```python:pagemaker/database.py
from configparser import ConfigParser

class Database(object):
    @classmethod
    def getProperties(cls, dbname):
        filename = dbname + ".ini"
        conf = ConfigParser()
        try:
            conf.read(filename)
            return conf["MailAddress"]
        except Exception:
            print("Warning: [{0}] is not found.".format(filename))
```
```python:maildata.init
[MailAddress]
hyuki@hyuki.com = Hiroshi Yuki
hanako@hyuki.com = Hananko Sato
tomura@hyuki.com = Tomura
mamoru@hyuki.com = Mamoru Takahashi
```
```python:pagemaker/html_writer.py
class HtmlWriter(object):
    def __init__(self, writer):
        self.writer = writer

    def title(self, title):
        self.writer.write("<html>\n")
        self.writer.write("<head>")
        self.writer.write("<title>{0}</title>".format(title))
        self.writer.write("</head>\n")
        self.writer.write("<body>\n")
        self.writer.write("<h1>{0}</h1>\n".format(title))

    def paragraph(self, msg):
        self.writer.write("<p>{0}</p>\n".format(msg))

    def link(self, href, caption):
        self.writer.write("<a href=\"{0}\">{1}</a>".format(href, caption))

    def mailto(self, mailaddr, username):
        self.link("mailto:{0}".format(mailaddr), username)

    def close(self):
        self.writer.write("</body>\n")
        self.writer.write("</html>\n")
        self.writer.close()
```
## (3) Client（依頼人）の役
Facadeパターンを利用する役です。
サンプルプログラムでは、`startMain`メソッドが、この役を努めます。

```python:Main.py
from pagemaker.page_maker import PageMaker

def startMain():
    PageMaker.makeWelcomePage("hyuki@hyuki.com", "welcome1.html")
    PageMaker.makeWelcomePage("mamoru@hyuki.com", "welcome2.html")

if __name__ == '__main__':
    startMain()
```

# ■ 参考URL
- [「Java言語で学ぶデザインパターン入門」を終えて(ない)](https://medium.com/since-i-want-to-start-blog-that-looks-like-men-do/java言語で学ぶデザインパターン入門-を終えて-ない-2cc9b34a30b2)
- [Qiita記事「デザインパターン（Design Pattern）#Facade](https://qiita.com/nirperm/items/9236663abc7e96736af4)