2012年12月19日水曜日

Scala Tips / トレイトでクラス分割

Scalaプログラミングの便利さを日々体感しているわけですが、その重要な要素の一つがトレイト。トレイトは関数型言語のものではなく、オブジェクト指向プログラミングの技術ですが、そのあるなしはプログラミング・モデルに大きく影響します。

Scalaにおけるクラス設計は、このトレイトの存在だけを意識するとしてもJavaのそれとは別ものということができます。

トレイトには色々な用途がありますが、最近ボクが地味に多用しているのがクラスの分割です。機能数が増えて大きくなったクラスを幾つかのトレイトに分割するだけなのですが、プログラムの見通しがよくなり、また機能間の依存関係も明確になるので、大変重宝しています。

クラス分割の例

以下のクラスがあるとします。変数aに設定した値をメソッドbとメソッドcが使用しています。

class Whole {
  val a = 1
  def b = a + 1
  def c = a + 2
}

このクラスを以下の3つの部品(クラス+トレイト)に分解し、これらの部品を組み立ててクラスを定義します。

Whole
クラス本体。変数aを定義している。
Parta
部品となるトレイト。変数aを使用するメソッドbを定義している。
Partb
部品となるトレイト。変数aを使用するメソッドcを定義している。
class Whole extends Parta with Partb {
  val a = 1
}

trait Parta {
  self: Whole =>

  def b = a + 1
}

trait Partb {
  self: Whole =>

  def c = a + 2
}

ポイントとなるのがPartaのメソッドbとPartbのメソッドcがクラスWholeの変数aに依存していること。普通はこういう場合、クラスWholeとトレイトb, cはインヘリタンスの関係が必要になってきます。

しかし、この場合はトレイトPartaとPartb内のself-type annotationである「self: Whole =>」によって、トレイトがWholeにmixinされることが保証されるためクラスWholeの変数aを使用することができます。

そしてクラスWholeの定義で「class Whole extends Parta with Partb」とすることで、クラスWholeにトレイトPartaとPartbの機能を追加することができます。

この場合、トレイトPartaとトレイトPartbの間には依存関係がないことが明確に分かります。このような形で、機能間の依存関係をできるだけ疎にしていくのが、大きなプログラムを作るときのコツとなります。

実行

実行結果は以下のとおりです。普通に動作しました。

scala> val w = new Whole
w: Whole = Whole@2bf75442

scala> w.a
res0: Int = 1

scala> w.b
res1: Int = 2

scala> w.c
res2: Int = 3

諸元

  • Scala 2.9.2

0 件のコメント:

コメントを投稿