Flyweight パターンとは
等価なインスタンスを別々の箇所で使用する際に、一つのインスタンスを再利用することによってプログラムを省リソース化することを目的とする。
flyweighという名がついているように、このデザインパターンは、オブジェクトを「軽く」するためのもの。 ここでいう重さとは、「メモリの使用量」のことを指す。たくさんメモリを使うオブジェクトを「重い」、省メモリのオブジェクトを「軽い」と表現する。
$obj = new Something();
Somethingクラスのインタスタンスを上記で作ることができる。このとき、そのインスタンスを保持するために、メモリが確保される。Somethingクラスのインスタンスがたくさん必要なときに、たくさんのインスタンスを生成してしまうと、メモリの使用量が大きくなる。
Flyweightパターンで使う技法は、一言で言えば「インスタンスをできるだけ共有して、無駄に new しない」というもの。
example
以下Flyweghtパターンを使ったサンプルプログラム。 重いインスタンスを作るクラスとして「大きな文字」を表現するクラスを考える。
- BigChar: 「大きな文字」を表すクラス
- BigCharFactory: BigCharのインスタンスを共有しながら生成するクラス
- BigString: BigCharを集めて作った「大きな文字列」を表すクラス
- Main: テスト動作用
BigCharクラス
BigCharは、ファイルから大きな文字のテキストを読み込んでメモリ上に保持し、printメソッドでそれを表示する。
<?php class BigChar { const DATA_DIR = __DIR__ . "/Data/"; private $charname; private $fontdata; public function __construct(string $charname) { $this->charname = $charname; try { $contents = file_get_contents(self::DATA_DIR . "big" . $charname . ".txt"); if ($contents === false) { throw new Exception("the file big{$charname}.txt does not exist."); } $this->fontdata = $contents; } catch (Exception $e) { echo "error: " . $e->getMessage(); } } public function print() { echo $this->fontdata; } }
BigCharFactoryクラス
BigCharFactoryクラスは、BigCharクラスのインスタンスを作る。しかし、同じ文字に対応するBigCharクラスのインスタンスがすでに作ってあった場合は、それを利用して、新しいインスタンスは作成しない。これまで作ったインスタンスはpoolプロパティに保持する。
<?php class BigCharFactory { private $pool = []; private static $singleton; private function __construct() { } public static function getInstance(): BigCharFactory { if (self::$singleton === null) { self::$singleton = new self; } return self::$singleton; } public function getBigChar(string $charname): BigChar { if (isset($this->pool[$charname]) === false) { $this->pool[$charname] = new BigChar($charname); } return $this->pool[$charname]; } }
BigStringクラス
BigStringはBigCharを集めて作った「大きな文字列」クラス。bigcharsプロパティはBigCharの配列であり、BigCharのインスタンスを保持する。
<?php class BigString { private $bigchars; public function __construct(string $string) { $this->bigchars = []; $factory = BigCharFactory::getInstance(); for ($i = 0; $i < strlen($string); $i++) { $this->bigchars[$i] = $factory->getBigChar($string[$i]); } } public function print() { for ($i = 0; $i < count($this->bigchars); $i++) { $this->bigchars[$i]->print(); } } }
Main
<?php $input = strval(trim(fgets(STDIN))); if (strlen($input) === 0) { echo "[input error]. please input the example below." . PHP_EOL; echo "example: 1212123" . PHP_EOL; die(); } $bs = new BigString($input); $bs->print();
実行結果
$ php Main.php
input digits: 1234
......##........
..######........
......##........
......##........
......##........
......##........
..##########....
................
....######......
..##......##....
..........##....
......####......
....##..........
..##............
..##########....
................
....######......
..##......##....
..........##....
......####......
..........##....
..##......##....
....######......
................
........##......
......####......
....##..##......
..##....##......
..##########....
........##......
......######....
................
ソースコード
役割
Flyweight役
普通に扱うとプログラムが重くなるので共有した方がよいものを表す役。 サンプルコードにおけるBigCharクラス。
FlyweightFactory役
Flyweight役を作る工場の役。この工場を使ってFlyweight役を作ると、インスタンスが共有される。 サンプルコードにおけるBigCharFactoryクラス。
Client役
FlyweightFactory役を使ってFlyweight役を作り出し、それを利用する役。 サンプルコードにおけるBigStringクラス。
まとめ
複数箇所に影響が及ぶ
Flyweightパターンではインスタンスを「共有」することがテーマ。インスタンスを共有するということは、共有しているものを変更すると、参照先全てに影響が及ぶということ。そのため、ほんとうに複数箇所に共有させるべき情報だけをFlyweight役に持たせるべき。
intrinsic と extrinsic を意識する
共有させる情報は、intrinsicな情報である。サンプルコードのBigCharフォントデータは、BigStringのどこに登場しても不変である。そのため、BigCharフォントデータはintrinsicな情報になる。 一方で、共有させない情報は、extrinsicな情報である。置く場所によって変化する情報は共有すべき情報ではない。Flyweightパターンを使う上では、これらを意識して区別する必要がある。
関連パターン
- PHP7でデザインパターン入門21/23 Proxyパターン - Do Something
- PHP7でデザインパターン入門11/23 Compositeパターン - Do Something
- PHP7でデザインパターン入門5/23 Singletonパターン - Do Something
- 作者: 結城浩
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2004/06/19
- メディア: 大型本
- 購入: 51人 クリック: 762回
- この商品を含むブログ (399件) を見る