RitoLabo

Flyweightパターン - PHPデザインパターン

  • 公開:
  • カテゴリ: PHP DesignPatterns
  • タグ: PHP,Factory,DesignPatterns,Structure,Singleton,Flyweight

Flyweightパターン(フライウェイト・パターン)は、構造に関するデザインパターン手法の1つで、同一のインスタンスを再利用する事でリソースを節約する処理モデルです。

Flyweightパターンの基本的なクラス図は以下になります。

Flyweightパターンクラス図

実装例

今回は、文字列情報を管理する仕組みをFlyweightパターンで実装していきます。

文字列情報、例えば「A」という文字列の場合は、それから変化のしようがなく、必ず「A」です。(ちなみに「A」や「a」は本質的には「A」とは別のものになります)なので、「A」という文字列情報を登録する場合、それに関する文字列情報は1つだけで良く、必要数分インスタンスを生成する必要はありません。こうした「不変」であるものに対してFlyweightパターンは有効です。

まずは文字列情報を保有するFlyweightクラスです。

App/Flyweight/Character.php
<?php

namespace App\Flyweight;

class Character
{
private $id;
private $character;

public function __construct($character)
{
$this->id = hash('sha256', time());
$this->character = $character;
}

public function getId()
{
return $this->id;
}

public function getCharacter()
{
return $this->character;
}
}

文字列情報を格納するだけなのでシンプルな構造です。

次に、Flyweightクラスを管理するFlyweightFactoryクラスです。

App/Factory/CharacterFactory.php
<?php

namespace App\Factory;

use App\Flyweight\Character;

class CharacterFactory
{
private static $instance;

private $Characters = [];

public static function getInstance()
{
if (empty(self::$instance)) {
self::$instance = new CharacterFactory();
}
return self::$instance;
}

public function addCharacter($char)
{
if (!array_key_exists($char, $this->Characters)) {
$this->Characters[$char] = new Character($char);
}
}

public function getCharacters()
{
return $this->Characters;
}

public final function __clone()
{
throw new \Exception('This Instance is Not Clone');
}

public final function __wakeup()
{
throw new \Exception('This Instance is Not unserialize');
}

}
  • Singletonパターンで作成します。
  • addCharacter()メソッドで文字列情報を登録する際に、既にその文字列が登録されていない場合に登録を行います。

実装が完了したので、クライアントから利用してみます。

index.php
<?php
require_once 'autoload.php';

use App\Factory\CharacterFactory;

$chars = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z'
];

$factory = CharacterFactory::getInstance();

for ($i=0; $i<20; $i++) {
$select_char = $chars[rand(0, 25)];
echo sprintf('%s / ', $select_char);
$factory->addCharacter($select_char);
}

echo '<hr>';

foreach ($factory->getCharacters() as $char_data) {
echo sprintf('文字列: %s<br>', $char_data->getCharacter());
}

今回は、アルファベット26文字をランダムに生成していき、それを登録していく、という流れになっています。

実行結果は以下になります。

PHPでFlyweightパターン実行結果

ランダムに選択された文字列(上部)を登録メソッドに渡していますが、それに対して、実際にインスタンス化されている文字列情報はユニークである事が確認できます。20個の文字列に対して、実際に生成したインスタンスは13個、インスタンスが節約されている事が確認できました。

まとめ

「Flyweight = フライ級」を意味する通り、不変なオブジェクトは無駄に生成せずに同一インスタンスで取りまわす事でリソースを節約、文字通り「軽く」するのがこのデザインパターンの目的です。

小規模なシステム・機能の場合は大した事はないですが、大規模な機能になるとその恩恵は多大なるものになってきます。 ただしアプリケーションが消費するリソースを節約する事は、イコール処理速度の向上にもつながるので、規模の大小に関わらず有効な手法です。是非試してみてください。