デザインパターンを覚えたい (Flyweight)
Flyweightパターン
共有で用いるインスタンスをプールし、使いまわすパターン。ポイントは
- 無駄なnewが減る
- メモリが節約できる
GCがない言語とかだとnewそのものが減るのは安全性の観点でも効果がありそう。個人的にはSingletonとあんま違わないように見えなくもない。
実装。今回は以下のような実装で速度比較してみる。
- Flyweightで共有するクラスとして以下のようなクラスを用意する
- コンストラクタ内でint型の1024要素の配列をnewする
- 中身は空のexecuteメソッドを実装する
- HogeFlyweight, FugaFlyweight, PuniFlyweightの3つのクラスを用意 (中身は同じ)
- 上記の3つのクラスの生成/実行を計1000000回実行し、プールする場合としない場合で速度を比較
- 要するに3 * 1000000 = 3000000回の生成/実行を行う
3つ同じクラスを用意しているのは、Flyweightっぽい実装にしたかっただけで特に意味はありません
http://dl.dropbox.com/u/7810000/code/design_pattern/flyweight.zip
実行。
$ ant test
Buildfile: /Users/xxx/tmp/flyweight/build.xmlbuild:
test:
[java] without flyweight : 2003 msec
[java] with flyweight : 118 msecBUILD SUCCESSFUL
Total time: 2 seconds
今回の実験条件ではFlyweightを使った方が速いことがわかります。
実装のポイントは、poolからインスタンスを再利用しているところ。
public class FlyweightFactory { private static FlyweightFactory instance = null; private Map<String, Flyweight> pool; private FlyweightFactory() { pool = new HashMap<String, Flyweight>(); } public static FlyweightFactory getInstance() { if (instance == null) instance = new FlyweightFactory(); return instance; } public Flyweight getFlyweight(String type, boolean usePool) { if (usePool) { if (pool.containsKey(type)) return pool.get(type); else { if (type == "hoge") pool.put(type, new HogeFlyweight()); else if (type == "fuga") pool.put(type, new FugaFlyweight()); else if (type == "puni") pool.put(type, new PuniFlyweight()); else return null; return pool.get(type); } } else { if (type == "hoge") return new HogeFlyweight(); else if (type == "fuga") return new FugaFlyweight(); else if (type == "puni") return new PuniFlyweight(); else return null; } } }
# nullのreturnとか実装が汚いのは勘弁してくだしあ><
ちなみに、はじめintの配列とかなにも生成しない空クラスで実験した場合はFlyweightを使わない方が速かったです。
これは、おそらくFlyweightのMapから要素を探索する部分がボトルネックになったせいで、
共有するインスタンスの生成コストによっては毎回生成するというスタンスもありなのかもしれません。