銀色うつ時間

思い出すたび何か胸につっかえてるだけ

Flyweightパターンの雑感とJavaおよびcoffeescriptでの実装

復習がてら殴り書きしつつ、Javacoffeescriptでの実装を貼っておく

メモ

  • 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変数の設定が間違っていたので修正した