9
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Scala:メソッドのポリモーフィズム

Last updated at Posted at 2016-01-12

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())

参考

9
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?