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

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

# ■ Flyweight（フライウェイト・パターン）
Flyweightパターン（フライウェイト・パターン）とは、GoFによって定義されたデザインパターンの1つである。 等価なインスタンスを別々の箇所で使用する際に、一つのインスタンスを再利用することによってプログラムを省リソース化することを目的とする。

## UML class and sequence diagram
![W3sDesign_Flyweight_Design_Pattern_UML.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/a4d6db2e-43cc-2944-b70e-f4ee25925eea.jpeg)
## UML class diagram
![flyweight.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/41164dc7-52bc-805e-1495-ed2df27186dd.png)
[(以上、ウィキペディア（Wikipedia）より引用)](https://ja.wikipedia.org/wiki/Flyweight_パターン)

## □ 備忘録
**flyweight**というのは、「フライ級」のことで、ボクシングで最も体重が軽い階級を示すものであり、このデザインパターンでは、オブジェクトを「軽く」するためのものだそうです。
`Flyweight`パターンで使っている技法は、「インスタンスをできるだけ共有させて、無駄にnewしない」というもので、インスタンスが必要なときに、いつもnewするのではなく、すでに作ってあるインスタンスを利用できるなら、それを共有して使うものだそうです。
直感的に、[デザインパターン「Singleton」](https://qiita.com/ttsubo/items/c4af71ceba15b5b213f8)の応用だと感じますね。

# ■ "Flyweight"のサンプルプログラム

##(1) 事前準備
アクキーアートっぽく、数字を表示するテキストファイルを準備しておきます。

```text:big0.txt
....######......
..##......##....
..##......##....
..##......##....
..##......##....
..##......##....
....######......
................
```
```text:big1.txt
......##........
..######........
......##........
......##........
......##........
......##........
..##########....
................
```
```text:big2.txt
....######......
..##......##....
..........##....
......####......
....##..........
..##............
..##########....
................
```
（以下、略）
## (2) 動作確認
実際に、Flyweightパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。

- 引数の数字の順番に、指定された数字をアスキーアートで表示する

```console
$ python Main.py 012123
....######......
..##......##....
..##......##....
..##......##....
..##......##....
..##......##....
....######......
................
......##........
..######........
......##........
......##........
......##........
......##........
..##########....
................
....######......
..##......##....
..........##....
......####......
....##..........
..##............
..##########....
................
......##........
..######........
......##........
......##........
......##........
......##........
..##########....
................
....######......
..##......##....
..........##....
......####......
....##..........
..##............
..##########....
................
....######......
..##......##....
..........##....
......####......
..........##....
..##......##....
....######......
................
```

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

- ディレクトリ構成

```console
.
├── 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`クラスが、この役を努めます。

```python:flyweight/big_char_factory.py
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`クラスが、この役を努めます。

```python:flyweight/big_char_factory.py
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`メソッドが、この役を努めます。

```python:flyweight/big_char_factory.py
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)
```
```python:Main.py
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])
```

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