1. Home
  2. PHP
  3. DesignPatterns
  4. Flyweightパターン - PHPデザインパターン

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

  • 公開日
  • カテゴリ:DesignPatterns
  • タグ:PHP,Factory,DesignPatterns,Structure,Singleton,Flyweight
Flyweightパターン - PHPデザインパターン

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

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 文字をランダムに生成していき、それを登録していく、という流れになっています。

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

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

まとめ

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

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

Author

rito

  • Backend Engineer
  • Tokyo, Japan
  • PHP 5 技術者認定上級試験 認定者
  • 統計検定 3 級