GoFのデザインパターンを学習する素材として、書籍「増補改訂版Java言語で学ぶデザインパターン入門」が参考になるみたいですね。ただ、取り上げられている実例は、JAVAベースのため、自分の理解を深めるためにも、Pythonで同等のプラクティスに挑んでみました。
■ Flyweight(フライウェイト・パターン)
Flyweightパターン(フライウェイト・パターン)とは、GoFによって定義されたデザインパターンの1つである。 等価なインスタンスを別々の箇所で使用する際に、一つのインスタンスを再利用することによってプログラムを省リソース化することを目的とする。
UML class and sequence diagram
UML class diagram
□ 備忘録
flyweightというのは、「フライ級」のことで、ボクシングで最も体重が軽い階級を示すものであり、このデザインパターンでは、オブジェクトを「軽く」するためのものだそうです。
Flyweight
パターンで使っている技法は、「インスタンスをできるだけ共有させて、無駄にnewしない」というもので、インスタンスが必要なときに、いつもnewするのではなく、すでに作ってあるインスタンスを利用できるなら、それを共有して使うものだそうです。
直感的に、デザインパターン「Singleton」の応用だと感じますね。
■ "Flyweight"のサンプルプログラム
##(1) 事前準備
アクキーアートっぽく、数字を表示するテキストファイルを準備しておきます。
....######......
..##......##....
..##......##....
..##......##....
..##......##....
..##......##....
....######......
................
......##........
..######........
......##........
......##........
......##........
......##........
..##########....
................
....######......
..##......##....
..........##....
......####......
....##..........
..##............
..##########....
................
(以下、略)
(2) 動作確認
実際に、Flyweightパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。
- 引数の数字の順番に、指定された数字をアスキーアートで表示する
$ python Main.py 012123
....######......
..##......##....
..##......##....
..##......##....
..##......##....
..##......##....
....######......
................
......##........
..######........
......##........
......##........
......##........
......##........
..##########....
................
....######......
..##......##....
..........##....
......####......
....##..........
..##............
..##########....
................
......##........
..######........
......##........
......##........
......##........
......##........
..##########....
................
....######......
..##......##....
..........##....
......####......
....##..........
..##............
..##########....
................
....######......
..##......##....
..........##....
......####......
..........##....
..##......##....
....######......
................
■ サンプルプログラムの詳細
Gitリポジトリにも、同様のコードをアップしています。
https://github.com/ttsubo/study_of_design_pattern/tree/master/Flyweight
- ディレクトリ構成
.
├── Main.py
├── big0.txt
├── big1.txt
├── big2.txt
├── big3.txt
├── big4.txt
├── big5.txt
├── big6.txt
├── big7.txt
├── big8.txt
├── big9.txt
└── flyweight
├── __init__.py
└── big_char_factory.py
(1) Flyweight(フライ級)の役
普通に扱うとプログラムが重くなるので共有した方が良いものを表す役です。
サンプルプログラムでは、BigChar
クラスが、この役を努めます。
class BigChar(object):
def __init__(self, charname):
try:
with open("big{0}.txt".format(charname), 'r') as txtfile:
data = txtfile.read()
self.__fontdata = data
except IOError:
self.__fontdata = charname + '?'
def __str__(self):
return self.__fontdata
(2) FlyweightFactory(フライ級の工場)の役
Flyweight
を作る工場の役です。この工場を使ってFlyweight
役を作ると、インスタンスが共有されます。
サンプルプログラムでは、BigCharFactory
クラスが、この役を努めます。
class BigCharFactory(object):
def __init__(self):
self.__pool = {}
@classmethod
def getInstance(cls):
if not hasattr(cls, "_instance"):
cls._instance = cls()
return cls._instance
def getBigChar(self, charname):
bc = self.__pool.get(charname)
if bc is None:
bc = BigChar(charname)
self.__pool[charname] = bc
return bc
(3) Client(依頼人)の役
FlyweightFactory
役を使ってFlyweight
を作り出し、それを利用する役です。
サンプルプログラムでは、BigString
クラスとstartMain
メソッドが、この役を努めます。
class BigString(object):
def __init__(self, string):
self.bigchars = []
self.factory = BigCharFactory.getInstance()
for s in string:
self.bigchars.append(self.factory.getBigChar(s))
def print(self):
for bc in self.bigchars:
print(bc)
import sys
from flyweight.big_char_factory import BigCharFactory, BigString
def startMain(string):
bs = BigString(string)
bs.print()
if __name__ == '__main__':
startMain(sys.argv[1])