ScalaでDDDなコードのアプリケーションを作ろうとしているときに UserId など値型はどうするべきか の記事を読み、「専用の値クラスを作る」のパターンでふと 「ここでケースクラスが AnyVal
を継承する理由ってなんだ...?」 と思ったので調べた。
case class PersonId(value: Long) extends AnyVal case class PersonName(value: String) extends AnyVal case class OrganizationId(value: Long) extends AnyVal case class Person(id: PersonId, name: PersonName, organizationId: OrganizationId) // ...
AnyValを継承すると
AnyValを継承すると値オブジェクトになる。値オブジェクトを継承したクラスはひとつの値しかとれない。
// OK class Melo(val a: Int) extends AnyVal // NG class Melo(val a: Int, val n: String) extends AnyVal
AnyVal
を使うことによって 実行時のオブジェクト割り当てを回避することができる ようになる。具体的には上の例でいうと Melo
クラスはコンパイル時は Melo
クラスだが、実行時は Int
として解釈される(アンボクシング)
だが、パターンマッチングなどで型検査が必要になると、 Int
としてアンボクシングされた値が再び Melo
としてボクシングされることになるため、パフォーマンスに影響を与える。
結論
雑に言うと AnyVal
を継承したクラスを使うとパフォーマンスが向上する っぽい。
個人的にはDDDにおける値オブジェクトをコードで表現するにあたって、もし 引数がひとつしかない のであれば、どんなケースでも AnyVal
を継承しない理由がないように思える... というか、調べていて値クラスという名前がたまたまDDDっぽいだけであって、これならべつにエンティティの実装だって可能なら AnyVal
継承クラスでえんちゃうの、と思ってしまった。
ただ、例えばバリデーションロジックで 「〜文字以下かどうかをチェックする」 みたいなビジネスルールがあったとき、クラスの中に MAXIMUM_LENGTH
みたいな定数を宣言するのは普通だが AnyVal
を継承しているクラスの中で val
による宣言ができないという制約がある。
コレに関しては、以下のようなメソッドを定義してしまえばいいのでは、という同期からのアイデアをもらったが、果たしてアリなのか...?
class Text(value: String) extends AnyVal { def MAXIMUM_LENGTH = 100 }