GoFのデザインパターンを学習する素材として、書籍「増補改訂版Java言語で学ぶデザインパターン入門」が参考になるみたいですね。ただ、取り上げられている実例は、JAVAベースのため、自分の理解を深めるためにも、Pythonで同等のプラクティスに挑んでみました。
■ Facade(ファサード・パターン)
Facadeパターンあるいは Façadeパターン(ファサード・パターン)とは、GoF(Gang of Four; 4人のギャングたち)によって定義された、コンピュータソフトウェアのデザインパターンの1つである。Facade(ファサード)とは「建物の正面」を意味する。異なるサブシステムを単純な操作だけを持ったFacadeクラスで結び、サブシステム間の独立性を高める事を目的とする。
UML class and sequence diagram
UML class diagram
□ 備忘録
Facadeパターンは、複雑に絡み合ってごちゃごちゃした詳細をまとめ、高レベルのインタフェース(API)を提供するそうです。Facade役は、システムの外側に対してはシンプルなインタフェースを見せるそうです。また、Facade役はシステム内側にある各クラスの役割を考えて、正しい順番でクラスを利用するそうです。
そういえば、昔のOpenStack Heat(オーケストレーション)の内部構造を調査していたときに、まさに、Facade
パターンに遭遇したのを思い出しました。
確か、 OpenStack heat-engine側では、SQLAlchemy経由でデータベースを利用していて、そのデータベース用セッションを、Facade
パターンで管理していたはずです。
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
■ "Facade"のサンプルプログラム
実際に、Facadeパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。
$ python Main.py
welcome1.html is created for hyuki@hyuki.com (Hiroshi Yuki)
welcome2.html is created for mamoru@hyuki.com (Mamoru Takahashi)
サンプルプログラムを起動すると、2つのhtmlファイルが生成されます。
おのおのを、Webブラウザを確認してみるとこんな感じになりました。
- welcome1.html
- welcome2.html
■ サンプルプログラムの詳細
Gitリポジトリにも、同様のコードをアップしています。
https://github.com/ttsubo/study_of_design_pattern/tree/master/Facade
- ディレクトリ構成
.
├── Main.py
├── maildata.ini
└── pagemaker
├── __init__.py
├── database.py
├── html_writer.py
└── page_maker.py
(1) Facade(正面)の役
Facade
役は、システムを構成しているその他大勢の役の「シンプルな窓口」となります。Facade
役は、高レベルでシンプルなインタフェースをシステム外部に提供します。
サンプルプログラムでは、PageMaker
クラスが、この役を努めます。
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
クラスが、この役を努めます。
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))
[MailAddress]
hyuki@hyuki.com = Hiroshi Yuki
hanako@hyuki.com = Hananko Sato
tomura@hyuki.com = Tomura
mamoru@hyuki.com = Mamoru Takahashi
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
メソッドが、この役を努めます。
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()