pythonによるデザインパターン[Abstract Factory]
# Intro
この文章は結城 浩さんの「Java言語で学ぶデザインパターン入門」を、pythonで実装してみたサンプルコードです。
筆者の環境は以下の通りです。Python 3.6.3
まだ修行中の身なので間違いがあると思いますがご了承ください。 今回は「Abstract Factory」です。
今回は他のものと比べて長いです。
# Explanation
本書のp96によると、 Abstract Factoryとは
抽象的な工場で、抽象的な部品を組み合わせて抽象的な製品を作る
というものです。全く分からないと思うので具体例を見た方が早いと思います。
# Practice
## Specification
python3 main.py list_factory.ListFactory
と入力すると
というhtml(以下ではList Styleと呼びます)を、
pyton3 main.py table_factory.TableFactory
と入力すると
というようなhtml(以下ではTable Styleと呼びます)を生成します。
## Explanation
どちらも内容は一緒で、表示するhtmlの形式が異なります。これから表示する形式が増える可能性があるので、まずは「抽象的にhtmlで文章を作る」ことについて考えましょう。
htmlで文章を作るときに以下のような関数に分けましょう
1. linkを作る — def create_link
2. trayを作る — def create_tray
3. pageを作る — def create_page
- linkを作るとは
<a href="http://...">Yahoo!</a>
のようにlinkを生成するということです。
2. tray(トレー・お盆)とは、htmlのかたまりのことです。例えば
<ul>
<li><a href="http://...">Yahoo!</a></li>
<li><a href="http://...">Yahoo!Japan</a></li>
</ul>
といった、 ul
の塊であったり、
<td>
<table width="100%" border="1">
<tr><td bgcolor="#cccccc" algin="center" colsapn="2"><b>新聞</b></td>
</table>
<td>
td
の塊のことです。
3. pageとはtrayを集めていってpage(html)全体を表すものです。
よって、 class Link
, class Tray
, class Page
をそれぞれ作り、そのクラス内で各部品を作っていきます。その上でその部品(class)を全て集めた class Factory
で
1. def create_link
— class Link
を返す
2. def create_tray
— class Tray
を返す
3. def create_page
— class Page
を返す
という風に実装します。あと便宜上 class Link
と class Tray
は類似点が多いので共通の親クラスとして class Item
を定義しますが、あまり気にしなくてもいいです。
これらを一つにまとめた抽象的な工場を abstract_factory.py
にまとめます。
次にListStyleを作りましょう。
先ほどの抽象classを継承した、
class ListFastory
class ListLink
class ListTray
class ListPage
の具体的な実装を書いていきます。抽象classを継承しているので、指定されたメソッドは全て実装するように注意してください。このListStyleを作る具体的な工場を list_factory.py
にまとめます。
次にTableStyleを作りましょう。とはいってもほぼListStyleと概要は同じで、htmlの内容が異なるだけです。
先ほどの抽象classを継承した、
class TableFastory
class TableLink
class TableTray
class TablePage
の具体的な実装を書いていきます。抽象classを継承しているので、指定されたメソッドは全て実装するように注意してください。このTableStyleを作る具体的な工場を table_factory.py
にまとめます。
最後にmain関数を作ります。main関数から扱うのは具体的な工場(line_factory.py
,table_factory.py
)ではなく、抽象的な工場( abstract_factory.py
)を扱います。よって、main関数からは具体的にどの実装がなされているのか気にする必要がありません。
main関数では以下の流れで、抽象的な工場( abstract_factory.py
)を扱います。
def get_factory
def create_link
def create_tray
def create_page
下の3つは先程説明したので良いとして、上の def get_factory
はabstract_factory.py
を説明するときに飛ばしていました。この関数は、 class Factory
のclass methodであり、与えられた文字列(今回はコマンドライン引数)に対応した具体的な工場を探してくる関数です。
return getattr(__import__(module), cla)()
重要なのは上の部分で、モジュールとクラス名を与えると、そのモジュールとクラス名に対応したクラスのインスタンスを取ってきます(このやり方がよく分からなかったので、tachikawa844の日記 を参考にさせて頂きました)。
そしてその取得した具体的な工場に
def create_link
def create_tray
def create_page
を施して、htmlを作成します。
## Sample Code
上で挙げたものをまとめたもので、内容は同じです。
# Conclusion
すごく長くなってしまい、結構端折ったのでよく分からないかもしれません笑。
重要なのは
- 関数の作成・インスタンスの作成を全て抽象化する
- 一つの具体例につき、抽象化したクラス・関数全てを継承し、具体的な工場を作る
- 呼び出し側は、具体的な工場をモジュール名・クラス名で取得して、その工場を扱う
といったところでしょうか。
注意しなければならないのは、新たな具体的な工場を追加するのは簡単ですが、全く新たな工場を追加するときに新しい関数やクラスが必要になると、全ての工場に影響が出ることです。例えば上の場合、新しいhtmlのフォーマットを追加するのは簡単ですが、表示するものをhtmlから画像にするフォーマットを追加しようと思うと、全ての工場のクラスや関数を修正しなければいけず、大変です(p116)。
Abstract Factoryは同じような形式・フォーマットが沢山増えていくが、新しい関数・機能が追加されることはほとんどないときに使えるのかもしれません。
# ref
- 「増補改訂版 Java言語で学ぶデザインパターン入門」 結城浩(著)第11版
- 「Abstract Factoryパターン — tachikawa844の日記」http://d.hatena.ne.jp/tachikawa844/20090110/1231561119