Flyweightパターンの雑感とJavaおよびcoffeescriptでの実装
復習がてら殴り書きしつつ、Javaとcoffeescriptでの実装を貼っておく
メモ
- GoFが定義したデザインパターンの1つ。同一のインスタンスを複数箇所で利用するときに、1つのインスタンスを再利用することで省リソース化することをねらう
- 共有したいインスタンスFlyweightと、キャッシュを保持しつつ必要に応じてFlyweightインスタンスを生成するFlayweightFactoryが想定される
- Factoryはシングルトンで実装される場面が多く、それによりキャッシュ機構が複数存在してしまうことを防ぐ
- Factoryの振舞いは次の通り
- 利用シーン
- イミュータブルなクラスを複数生成したい場合など
- 注意点
- 一度 FlyweightFactory に保存されたインスタンスは、たとえ不要になった場合でもガベージコレクションされることがないため、場合によっては明示的に FlyweightFactory から削除する必要がある
- Factoryの裏でシングルトンをいくつも存在させたいという場面で、自然と思いついていそうなパターンである。
- プリミティブでないものをキャッシュ機構に載せるということは、参照を保持するということなので、もし参照先で変更が生じた場合、それは参照元にも影響を与える。こう考えるとよくあるバグを生みそうなパターンである。
Javaによる実装
- 素直
package com.sisidovski.flyweight; public class HumanLetter { private String letter; public HumanLetter(String character) { this.letter = character; } public void display() { System.out.println(letter); } } package com.sisidovski.flyweight; import java.util.HashMap; import java.util.Map; public class HumanLetterFactory { private Map<String, HumanLetter> map = new HashMap<String, HumanLetter>(); public static HumanLetterFactory singleton = new HumanLetterFactory(); private HumanLetterFactory(){} public HumanLetterFactory getInstance() { return singleton; } public int countInstance() { return this.map.size(); } public synchronized HumanLetter getHumanLetter(String letter) { HumanLetter humanLetter = map.get(letter); if (humanLetter == null) { humanLetter = new HumanLetter(letter); map.put(letter, humanLetter); } return humanLetter; } } package com.sisidovski.flyweight; public class Test { public static void main(String args[]) { HumanLetterFactory factory = HumanLetterFactory.singleton; factory.getHumanLetter("a").display(); factory.getHumanLetter("b").display(); factory.getHumanLetter("c").display(); factory.getHumanLetter("d").display(); factory.getHumanLetter("e").display(); factory.getHumanLetter("a").display(); factory.getHumanLetter("b").display(); factory.getHumanLetter("c").display(); factory.getHumanLetter("d").display(); factory.getHumanLetter("e").display(); System.out.println(factory.countInstance()); } }
coffeescriptによる実装
- どちらかと言うとsingletonの実装に躓いた
- 生jsでの実装は結構辛そうだが、時間があるときにやってみたい
class FlyWeight constructor: (string) -> @string = string display: -> console.log @string class Factory map = {} instance = null class Private getFlyWeight: (string)-> flyweight = map[string] unless flyweight flyweight = new FlyWeight(string) map[string] = flyweight return flyweight count: -> Object.keys(map).length @get: -> instance ?= new Private() # test factory = Factory.get(); factory.getFlyWeight('a').display() factory.getFlyWeight('b').display() factory.getFlyWeight('c').display() factory.getFlyWeight('d').display() factory.getFlyWeight('e').display() factory.getFlyWeight('a').display() factory.getFlyWeight('b').display() factory.getFlyWeight('c').display() factory.getFlyWeight('d').display() factory.getFlyWeight('e').display() count = factory.count() console.log count
追記
coffeescriptの例でinstance変数の設定が間違っていたので修正した