デザインパターン入門(Flyweight)


この記事ではFlyweightについてまとめます。
wikipediaによると「等価なインスタンスを別々の箇所で使用する際に、一つのインスタンスを再利用することによってプログラムを省リソース化する」とあります。
参考:Flyweightパターン

主な登場人物

NO 名前 役割
1 Flyweightクラス インスタンス化したいクラス
2 FlyweightFactoryクラス インスタンス化したクラスの状態を保持する

プログラムで森を表現する場合、木のインスタンスを大量に生成すると思います。大量に生成することでサーバ上のメモリを消費してしまいますが、実行時のインスタンス数を削減してメモリを節約するパターンとなります。Javaではメモリ節約のチューニング案として、String.internというメソッドがありましたが考え方(同じ内容のオブジェクトを使い回す)はほぼ同じかと存じます。

パターンを実装する
目的は単純でインスタンス化(ソースコード上でnewする)を減らすことです。wikipediaの例で実装いたします。ある文字列をchar型で一文字づつ定義していき、すでに定義されている文字列であれば使い回す、定義されていなければ生成するプログラムとします。

Flyweightクラス

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

FlyweightFactoryクラス

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

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

実行クラス

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

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();
        }
    }
}
結果
たかいたけたてかけた

できるだけメモリ利用を削減するような使い回し処理を入れると、今度はオーバーヘッドや全体のパフォーマンス部分にしわ寄せが行く可能性があるため、それぞれの落とし所を見極める必要があります。ただ、Java言語の場合1つのオブジェクトを生成する為に、文字(char型)だと2バイト、整数(int型)だと4バイトメモリを消費します。それが並列にアクセスされる環境だと一時的とは言っても使用量が数倍になるため意識しておくべき事象(パターン)かと思います。