目次へ

20.Flyweight パターン

  • 2012/04/26 一部修正しました

20.1 Flyweight パターンとは

第20章では Flyweight パターンを学びます。Flyweight とは、英語で「フライ級」を意味する単語です。 フライ級とは、ボクシングなどの体重階級の中でもっとも軽い部類に分類できる階級です。もっと軽い階級もありますが。 Flyweight パターンとは、インスタンスを共有することでリソースを無駄なく使うことに焦点を当てたパターンです。

例えば、ホームページの背景に使う小さな画像は、背景で表示される回数分ネットワークごしにやり取りされるわけではありません。 普通は、画像を一回取得し、「その画像」を並べて表示するわけです。

Flyweight パターンとは、このように、同じインスタンスを共有することで、無駄なインスタンスを生成しないようにして、 プログラム全体を軽くすることを目的としたパターンなのです。

20.2 サンプルケース

サンプルケースでは、卒業記念にみんなの思い出として作成することになった「人文字」を使ったメッセージについて考えて見ます。
あなたのクラスでは、「あいはあおよりもあおい」というメッセージを作ることになりました。何も伝わってきませんが・・・。
一文字ずつ「人文字」で作って屋上から撮影し写真に残していきます。一文字の人文字を表すクラスを HumanLetter クラスとすると、 人文字でメッセージを生成するクラスは、以下のようになるでしょうか。

/**
 * 人文字を作成して出力するクラス。
 */
public class Main {
    /**
     * 人文字を作成するメイン関数
     * @param args
     */
    public static void main(String args[]) {
        HumanLetter あ = new HumanLetter("あ");
        takeAPhoto(あ);
        HumanLetter い = new HumanLetter("い");
        takeAPhoto(い);
        HumanLetter は = new HumanLetter("は");
        takeAPhoto(は);
        HumanLetter あ2 = new HumanLetter("あ");
        takeAPhoto(あ2);
        HumanLetter い2 = new HumanLetter("い");
        takeAPhoto(い2);
        HumanLetter よ = new HumanLetter("よ");
        takeAPhoto(よ);
        HumanLetter り = new HumanLetter("り");
        takeAPhoto(り);
        HumanLetter も = new HumanLetter("も");
        takeAPhoto(も);
        HumanLetter あ3 = new HumanLetter("あ");
        takeAPhoto(あ3);
        HumanLetter お = new HumanLetter("お");
        takeAPhoto(お);
        HumanLetter い3 = new HumanLetter("い");
        takeAPhoto(い3);
    }

    /**
     * 写真を撮るメソッド
     * @param letter
     */
    private static void takeAPhoto(HumanLetter letter) {
        System.out.println(letter.getLetter());
    }

}

このように「あ」「い」の文字を何度も生成することになります。写真に取るだけであれば、 最初に生成した「あ」インスタンスや「い」インスタンスを使いまわすことができそうです。 この点を考慮すると、Main クラスは、以下のように変更することができるでしょう。

/**
 * 人文字を作成して出力するクラス。
 */
public class Main {
    /**
     * 人文字を作成するメイン関数
     * @param args
     */
    public static void main(String args[]) {
        HumanLetter あ = new HumanLetter("あ");
        takeAPhoto(あ);
        HumanLetter い = new HumanLetter("い");
        takeAPhoto(い);
        HumanLetter は = new HumanLetter("は");
        takeAPhoto(は);
        takeAPhoto(あ);
        takeAPhoto(い);
        HumanLetter よ = new HumanLetter("よ");
        takeAPhoto(よ);
        HumanLetter り = new HumanLetter("り");
        takeAPhoto(り);
        HumanLetter も = new HumanLetter("も");
        takeAPhoto(も);
        takeAPhoto(あ);
        HumanLetter お = new HumanLetter("お");
        takeAPhoto(お);
        takeAPhoto(い);
    }

    /**
     * 写真を撮るメソッド
     * @param letter
     */
    private static void takeAPhoto(HumanLetter letter) {
        System.out.println(letter.getLetter());
    }

}

コストのかかるインスタンス化(ここでは人を並び替えて文字を形成するコンストラクタ。 本来ならDBアクセスなどが考えられれる。)の回数が少し減りました。 当然インスタンスの数も減っています。
プログラムが少し軽量化されているのが分かりますね。 この軽量化をうまく使うために、Flyweight パターンを利用します。 Flyweight パターンでは、軽量化されるべきインスタンスの生成や管理を行う Factory クラスを作成し、 軽量化されるべきインスタンスは、この Factory クラスを介して取得するようにします。
クラス図にすると以下のようになります。

クラス図

Factory クラスのソースコードは、以下のようになります。

import java.util.HashMap;
import java.util.Map;

/**
 * 人文字を管理するクラス。
 * Singleton とする。
 */
public class HumanLetterFactory {
    /**
     * 人文字のマップ
     */
    Map<String,HumanLetter> map = new HashMap<String,HumanLetter>(); 

    /**
     * Singleton の生成
     */
    private static HumanLetterFactory singleton = new HumanLetterFactory();

    /**
     * Singleton パターン
     */
    private HumanLetterFactory() {}

    /**
     * インスタンスを取得するメソッド
     * 唯一のインスタンスが返される。
     * @return singleton
     */
    public HumanLetterFactory getInstance() {
        return singleton;
    }

    /**
     * 人文字を取得するメソッド。
     * すでに持っているものであれば、map から返す。
     * map に持っていないものは生成して map への登録を行った後に返り値として返す。
     * 
     * @param letter
     * @return humanLetter
     */
    public synchronized HumanLetter getHumanLetter(String letter) {
        HumanLetter humanLetter = map.get(letter);
        if (humanLetter == null) {
            humanLetter = new HumanLetter(letter);
            map.put(letter, humanLetter);
        }
        return humanLetter;
    }

}
    

Factory クラスを Singleton にすることで、複数の Factory クラスが生成されてしまうことを防ぎます。 HumanLetterFactory クラスの getHumanLetter メソッドでは、管理している map に照会し、すでに持っている 文字を表すインスタンスが必要な際は、わざわざ新しく作り直すことはありません。もし、もっていない文字を 求められた場合は、新たにインスタンス化し、map に登録の上、生成したインスタンスを返します。
Flyweight パターンを利用することで、どのインスタンスを持っているのかなどを、利用者(Main クラスのような呼び出し側) で把握しておく必要がなくなります。また、複数の呼び出し先からの要求にも無駄なく応えることが可能となります。

Flyweight パターンは、ひとつのインスタンスを各所で共有することになりますので、 共有されるインスタンスが本質的(intrinsic)な情報のみを持つ場合にのみ利用するべきです。 非本質的(extrinsic)な情報は、どこかの誰かが変更する可能性のあるものであり、 共有するインスタンスを勝手に誰かが変更することで、そこかしこに影響を与えることになりかねません。

20.3 Flyweight パターンまとめ

Flyweight パターンは、無駄なインスタンスを生成を防ぎ、共有することでリソースを無駄なく使います。

Flyweight パターンの一般的なクラス図は、以下のようになります。

クラス図
[引用] 『Java言語で学ぶ デザインパターン入門』(結城浩 ソフトバンクパブリッシング株式会社出版 2001年)

↑このページの先頭へ

こちらもチェック!

PR
  • XMLDB.jp