ScalaでCompositeパターン

30分プログラム、その448。Scalaのcase classを使って、Compositeパターンを作ってみる。

ディレクトリみたいなツリー状になっているデータ構造を表現するのに、とっても便利なCompositeパターンをScalaで書いてみよう。

まずは普通に定義しよう。

まずは、増補改訂版Java言語で学ぶデザインパターン入門に載っているコードを、そのままScalaに移してみよう。ただ、全部マネすると大変なので、機能をサイズ取得だけにしぼる。
UMLにするとこんな感じ。

abstract class Entry{
  def size : int
}

class File(n : int) extends Entry{
  def size : int = n
}
class Directory extends Entry{
  import scala.collection.mutable.ListBuffer
  private val directory = new ListBuffer[Entry]

  def size : int = {
    directory.foldLeft(0)(_ + _.size)
  }
  def add(e : Entry)  : Unit = {
    directory += e
  }
}

val root = new Directory
val bin  = new Directory

bin.add(new File(100))
bin.add(new File(200))
root.add(bin)

println(root.size)

case classを使って書き直す

さて、Scalaにはcase classというステキな機能がある。これは

  • ファクトリメソッドが自動で生成される(new不要)
  • パラメータがvalでえ宣言したのと同じに扱われる。(getterの自動生成)
  • toString/hashcodeが自動で生成される
  • パターンマッチができる

みたいなやつ。

これを使ってCompositeパターンを定義しなおしてみよう。

sealed abstract class Entry
case class File(size : int) extends Entry
case class Directory(directory : List[Entry]) extends Entry

def size(entry : Entry) : int = {
  entry match {
    case Directory(directory) =>
      directory.foldLeft(0)(_ + size(_))
    case File(size : int) =>
      size
  }
}

val bin  = Directory(List(File(100),File(200)))
val root = Directory(List(bin))
println(size(root))

うん、とってもすっきり。

要するに

デザインパターンも大事だけど、言語側からのサポートがあったほうがハッピーだよね。