Flyweight パターン

Flyweight パターン（フライウェイト・パターン）とは、GoFによって定義されたデザインパターンの1つである。等価なインスタンスを別々の箇所で使用する際に、一つのインスタンスを再利用することによって計算資源の浪費を減らすことを目的とする。なお、とは、英語で「フライ級」を意味し、ボクシングにおける体重別階級の1つである。

クラス図 
Flyweight パターンのクラス図を以下に挙げる。

概要 
Flyweight パターンで設計された API では、利用者は Flyweight クラスにあたるインスタンスを取得する場合に、直接そのクラスのコンストラクタを呼び出す代わりに FlyweightFactory.getFlyweight() にアクセスする。

一方、呼び出された FlyweightFactory オブジェクトは、状況に応じて以下のように振舞う。
その時点で対象のインスタンスが生成されていない場合
対象のインスタンスを新たに生成する。
生成したインスタンスをプールする（言い換えると、メンバのコンテナオブジェクトに格納する）。
生成されたインスタンスを返す。
対象のインスタンスが既に生成されていた場合
対象のインスタンスをプールから呼び出す。
対象のインスタンスを返す。

このように FlyweightFactory では状況によって異なる処理が行われるが、利用者側が得る結果は全く同じであるため、利用者は FlyweightFactory の内部構造を意識せずに使うことができるという点が特徴である。

Flyweight パターンを採用すべき典型的な例は、不変なクラスを扱う場合である。不変なクラスとはインスタンスが生成された後にそのインスタンスの状態が変化しないようなクラスであり、Java では  や  などが挙げられる。詳しくはイミュータブルを参照。

利用例 
Java による Flyweight パターンの例を挙げる。このソースコードは Java 1.5 以降のバージョンで動作する。

import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;

class Stamp {
    private final char type;
    public Stamp(char type) {
        this.type = type;
    }
    public void print() {
        System.out.print(this.type);
    }
}

class StampFactory {
    private final Map<Character, Stamp> pool;
    public StampFactory() {
        this.pool = new HashMap<Character, Stamp>();
    }
    public Stamp get(char type) {
        Stamp stamp = this.pool.get(type);
        if (stamp == null) {
            stamp = new Stamp(type);
            this.pool.put(type, stamp);
        }
        return stamp;
    }
    public int getPoolSize() {
        return this.pool.size();
    }
}

public class FlyweightTest {
    public static void main(String[] args) {
        StampFactory factory = new StampFactory();
        List<Stamp> stamps = new ArrayList<Stamp>();
        stamps.add(factory.get('た'));
        stamps.add(factory.get('か'));
        stamps.add(factory.get('い'));
        stamps.add(factory.get('た'));
        stamps.add(factory.get('け'));
        stamps.add(factory.get('た'));
        stamps.add(factory.get('て'));
        stamps.add(factory.get('か'));
        stamps.add(factory.get('け'));
        stamps.add(factory.get('た'));
        for (Stamp s : stamps) {
            s.print();
        }
        System.out.println();
        System.out.println("Factory pool size = " + factory.getPoolSize());
    }
}

ソースコードとクラス図の対応関係を示す。

 Flyweight
 Stamp
 FlyweightFactory
 StampFactory
 FlyweightFactory.getFlyweight()
 StampFactory.get()

このプログラムを実行すると「たかいたけたてかけた」（高い竹立て掛けた）という文字列を出力する。FlyweightTest.main() 内で StampFactory.get() を 10 回参照しているが、そのうち実際に生成されたインスタンスは 5 つ（「た」、「か」、「い」、「け」、「て」）だけであり、インスタンスが共有されていることが分かる。

注意点 
一度 FlyweightFactory に保存されたインスタンスは、たとえ不要になった場合でもガベージコレクションされることがないため、場合によっては明示的に FlyweightFactory から削除する必要がある。

関係するパターン 
 Singleton パターン
 FlyweightFactory は Singleton として実装されることが多い。

関連項目 
 デザインパターン (ソフトウェア)
 ガベージコレクション
 イミュータブル

外部リンク 
 Javaの理論と実践: 可変性か、不変性か? | IBM, Internet Archive

デザインパターン (ソフトウェア)
ソフトウェア最適化