scalaの多相いろいろ - おみブロZという素晴らしい記事を参考に、メソッドのポリモーフィズムの実装パターンを自分なりにまとめてみる。
まず、「メソッドのポリモーフィズム」という言葉が一般的でないかもしれないので、この記事での意味として、「引数や戻り値の型が異なる同名のメソッドを実装する方法」としておく。
また、ここで例示する記事は、GitHubのsuin/scala-playgroundに置いてあるので、試したい方はgit pullしてみてください。
メソッドのオーバーロード
オーバーロードはJavaなどにもある仕組みで、引数の型が違う場合、同じ名前のメソッドを定義できる。アドホック多相(Ad hoc polymorphism)。
case class CreateCommand()
case class UpdateCommand()
case class CreateResult(createOk: Boolean)
case class UpdateResult(updateOk: Boolean)
class CommandHandler {
def handle(createCommand: CreateCommand): CreateResult = CreateResult(true)
def handle(updateCommand: UpdateCommand): UpdateResult = UpdateResult(true)
}
val handler = new CommandHandler()
val result1: CreateResult = handler.handle(CreateCommand())
val result2: UpdateResult = handler.handle(UpdateCommand())
継承(Commandパターン)
オブジェクト指向言語で見かけるデザインパターン。サブタイプ多相(Subtype polymorphism)。
case class CreateCommand()
case class UpdateCommand()
case class CreateResult(createOk: Boolean)
case class UpdateResult(updateOk: Boolean)
trait CommandHandler[C, R] {
def handle(command: C): R
}
class CreateCommandHandler extends CommandHandler[CreateCommand, CreateResult] {
override def handle(command: CreateCommand): CreateResult = CreateResult(true)
}
class UpdateCommandHandler extends CommandHandler[UpdateCommand, UpdateResult] {
override def handle(command: UpdateCommand): UpdateResult = UpdateResult(true)
}
def apply() = {
val handler1 = new CreateCommandHandler()
val handler2 = new UpdateCommandHandler()
val result1: CreateResult = handler1.handle(CreateCommand())
val result2: UpdateResult = handler2.handle(UpdateCommand())
Pimp my Library Pattern
Scalaの暗黙の型変換を使って、クラスにメソッドを追加する方法。
case class CreateCommand()
case class UpdateCommand()
case class CreateResult(createOk: Boolean)
case class UpdateResult(updateOk: Boolean)
class CommandHandler
implicit class CreateCommandHandler(handler: CommandHandler) {
def handle(command: CreateCommand): CreateResult = CreateResult(true)
}
implicit class UpdateCommandHandler(handler: CommandHandler) {
def handle(command: UpdateCommand): UpdateResult = UpdateResult(true)
}
val handler = new CommandHandler()
val result1: CreateResult = handler.handle(CreateCommand())
val result2: UpdateResult = handler.handle(UpdateCommand())
Magnet Pattern (implicit parameter)
型クラス + Dependent Method Types + implicit parameterで実現するMagnet Pattern。
case class CreateCommand()
case class UpdateCommand()
case class CreateResult(createOk: Boolean)
case class UpdateResult(updateOk: Boolean)
trait Command[A, R] {
type Result = R
def apply(command: A): Result
}
class CommandHandler {
def handle[A, R](command: A)(implicit handler: Command[A, R]): handler.Result = handler(command)
}
implicit val createCommand = new Command[CreateCommand, CreateResult] {
def apply(command: CreateCommand): Result = CreateResult(true)
}
implicit val updateCommand = new Command[UpdateCommand, UpdateResult] {
def apply(command: UpdateCommand): Result = UpdateResult(true)
}
val handler = new CommandHandler()
val result1: CreateResult = handler.handle(CreateCommand())
val result2: UpdateResult = handler.handle(UpdateCommand())
Magnet Pattern (implicit conversion)
型クラス + Dependent Method Types + implicit conversionで実現するMagnet Pattern。
import scala.language.implicitConversions
case class CreateCommand()
case class UpdateCommand()
case class CreateResult(createOk: Boolean)
case class UpdateResult(updateOk: Boolean)
trait CommandMagnet[R] {
type Result = R
def apply(): Result
}
implicit def createCommandHandler(createCommand: CreateCommand): CommandMagnet[CreateResult] = new CommandMagnet[CreateResult] {
def apply(): Result = CreateResult(true)
}
implicit def updateCommandHandler(updateCommand: UpdateCommand): CommandMagnet[UpdateResult] = new CommandMagnet[UpdateResult] {
def apply(): Result = UpdateResult(true)
}
class CommandHandler {
def handle[T](magnet: CommandMagnet[T]): magnet.Result = magnet()
}
val handler = new CommandHandler()
val result1: CreateResult = handler.handle(CreateCommand())
val result2: UpdateResult = handler.handle(UpdateCommand())