Flyweight パターンの Scala での実装例

Written by @dr_taka_n at 2010/12/30 13:20 []

重たい処理の結果をキャッシュして、無駄な処理を行わないようにする実装パターン。GoF による 23 のデザインパターンでは、Flyweight パターンと呼ばれている。

上記 Wikipedia に Java での実装例があるが、これを Scala で実装してみる。

このパターンを使うメリットは、

  • オブジェクトの生成結果をキャッシュするため、オブジェクトの生成コストが下がる
    既に生成されているものは再利用、生成されていなければ生成。
  • 余計なインスタンスをバコバコ作らないため、エコ

というところ。注意点としては、

  • キャッシュするオブジェクトはイミュータブル(immutable)であること
    キャッシュされているオブジェクトはいろいろなオブジェクトで共有されることが想定される。変更されないようにしておく。
  • 増え続けるキャッシュをどうするか
    キャッシュされたオブジェクトは GC の対象にならず、VM が生きている限りメモリに残り続ける。何をキャッシュするのか、しっかり考える必要がある。

Flyweight パターンの登場人物は、

  • Flyweight
  • FlyweightFactory
    FlyweightFactory#getFlyweight
  • Client

の3者となり、以下の例では、Flyweight -> Stamp、FlyweightFactory -> StampFactory、Client -> FlyweightPatternExample となる。

import scala.collection.mutable

// Flyweight
class Stamp(t: Char) {
  def print = System.out.print(t)
}

// FlyweightFactory
object StampFactory {
  val pool = mutable.Map.empty[Char, Stamp]

  def get(t: Char): Stamp = pool.get(t) match {
    case Some(s: Stamp) => s
    case None =>
      val stamp = createStamp(t)
      pool += (t -> stamp)
      stamp
  }

  private def createStamp(t: Char): Stamp = {
    Thread.sleep(1000) // 実は重たい処理。。
    new Stamp(t)
  }
}

// Client
object FlyweightPatternExample {
  def main(args: Array[String]) = {
    val stamps = mutable.ArrayBuffer.empty[Stamp]

    val chars = "たかいたけたてかけた".toCharArray

    println("-" * 5 + " collecting ...")
    chars.foreach { c =>
      val start = System.nanoTime
      stamps += StampFactory.get(c)
      val end = System.nanoTime
      println("'%s' (%d)".format(c, end - start))
     }

    println("-" * 5 + " output")
    stamps.foreach { e =>
      e.print
      println(" " + e.toString)
    }
  }
}

実行結果は以下の通り。

[info] Running FlyweightPatternExample 
----- collecting ...
'た' (1002272250)
'か' (1000112222)
'い' (1000181113)
'た' (73671)
'け' (1000181459)
'た' (8248)
'て' (1000120234)
'か' (7799)
'け' (5361)
'た' (5243)
----- output
た Stamp@11c537f
か Stamp@adcae8
い Stamp@1e4605c
た Stamp@11c537f
け Stamp@727896
た Stamp@11c537f
て Stamp@14ace71
か Stamp@adcae8
け Stamp@727896
た Stamp@11c537f

上記の結果から、

  • オブジェクトの取得(StampFactory.get)は10回行われているが、生成されたインスタンスは5つ。
  • 同じオブジェクトの2回目以降の取得コストはキャッシュから取得しているため、軽くなっている。

という点がわかる。

blog comments powered by Disqus