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

  1. 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_linkclass Link を返す

2. def create_trayclass Tray を返す

3. def create_pageclass Page を返す

という風に実装します。あと便宜上 class Linkclass 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_factoryabstract_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

--

--