如何只知道它的超类?我怎样才能找到正确的隐式CommandHandler
类型?
在MainRunner类中,您可以看到以下代码要求我实现AllCommandHandler
类型类,它在所有类型上进行模式匹配。有没有办法可以为超类型UserCommand
找到合适的类型类而不实现AllCommandsHandler
?
val command: UserCommand = CreateUser("userId2")
CommandHandlerRunner.processCommand(command)
我需要这个的原因是因为我的代码的某些部分得到List[UserCommand]
,它们来自数据库或在验证输入命令后生成。
我的代码
import annotation.implicitNotFound
@implicitNotFound("No member of type class CommandHandler in scope for ${C}")
trait CommandHandler[C <: UserCommand] {
def processCommand(command: C)
}
trait Command
sealed trait UserCommand
case class CreateUser(id: String) extends UserCommand
case class UpdatePassword(id: String, password: String) extends UserCommand
object CommandHandler {
implicit object CreateUserCommandHandler extends CommandHandler[CreateUser] {
override def processCommand(command: CreateUser) = println(command)
}
implicit object UpdateUserPasswordCommandHandler extends CommandHandler[UpdatePassword] {
override def processCommand(command: UpdatePassword) = println(command)
}
//I have to do this which is ugly. Is there a way I can invoke the right CommandHandler without doing the following ?
implicit object AllCommandsHandler extends CommandHandler[UserCommand] {
override def processCommand(command: UserCommand) = command match {
case command: CreateUser =>
CommandHandlerRunner.processCommand(command)
case command: UpdatePassword =>
CommandHandlerRunner.processCommand(command)
case _ =>
sys.error("CommandHandler not found.")
}
}
}
object CommandHandlerRunner {
def processCommand[C <: UserCommand](command: C)(implicit commandHandler: CommandHandler[C]) =
commandHandler.processCommand(command)
}
object MainRunner extends App {
CommandHandlerRunner.processCommand(CreateUser("userId1"))
CommandHandlerRunner.processCommand(UpdatePassword("userId1", "newPassword"))
//In my application in certain situation I get a List[UserCommand] (database query) and I want to invoke processCommand their relevant
//handlers. How do I get this to work ?
val command: UserCommand = CreateUser("userId2")
CommandHandlerRunner.processCommand(command)
}
答案 0 :(得分:4)
以下CommandHandler
的{{1}}推导有效,因为您可以将UserCommand
表示为UserCommand
或CreateUser
(这很重要UpdatePassword
是UserCommand
)。
sealed
的通用表示是UserCommand
。CreateUser :+: UpdatePassword :+: CNil
和cnilCommandHandler
可以为此通用表示获取coproductConsCommandHandler
个实例。CommandHandler
使用此通用表示形式的实例为genericCommandHandler
提供实例(使用UserCommand
)。从Generic[UserCommand]
(CommandHandler
):
<: UserCommand
现在我们可以使用您的import shapeless._
trait CommandHandler[C] {
def processCommand(command: C): Unit
}
object CommandHandler {
// make it possible to derive a CommandHandler instance for sealed traits
// like UserCommand using shapeless Coproduct and Generic
implicit val cnilCommandHandler: CommandHandler[CNil] =
new CommandHandler[CNil] {
override def processCommand(t: CNil): Unit = ()
}
implicit def coproductConsCommandHandler[L, R <: Coproduct](implicit
lch: CommandHandler[L],
rch: CommandHandler[R]
): CommandHandler[L :+: R] =
new CommandHandler[L :+: R] {
override def processCommand(t: L :+: R): Unit = t match {
case Inl(l) => lch.processCommand(l)
case Inr(r) => rch.processCommand(r)
}
}
implicit def genericCommandHandler[A, G](implicit
gen: Generic.Aux[A, G],
cch: Lazy[CommandHandler[G]]
): CommandHandler[A] =
new CommandHandler[A] {
def processCommand(a: A): Unit = cch.value.processCommand(gen.to(a))
}
}
:
UserCommand
...使用sealed trait UserCommand
final case class CreateUser(id: String) extends UserCommand
final case class UpdatePassword(id: String, password: String) extends UserCommand
object UserCommand {
implicit val createUserCommandHandler: CommandHandler[CreateUser] =
new CommandHandler[CreateUser] {
override def processCommand(command: CreateUser) = println(command)
}
implicit val updateUserPasswordCommandHandler: CommandHandler[UpdatePassword] =
new CommandHandler[UpdatePassword] {
override def processCommand(command: UpdatePassword) = println(command)
}
}
,即使类型为CommandHandlerRunner
:
UserCommand
这可能比您object CommandHandlerRunner {
def processCommand[C](command: C)(implicit commandHandler: CommandHandler[C]) =
commandHandler.processCommand(command)
}
val cmd1: UserCommand = CreateUser("foo")
val cmd2: UserCommand = UpdatePassword("id", "open sesame")
CommandHandlerRunner.processCommand(cmd)
// CreateUser(foo)
List(cmd1, cmd2).foreach(CommandHandlerRunner.processCommand[UserCommand] _)
// CreateUser(foo)
// UpdatePassword(id,open sesame)
的代码多,但如果您现在创建AllCommandHandler
如下:
DBCommand
为sealed trait DBCommand
final case class Get(id: Int) extends DBCommand
final case class Save[A](a: A) extends DBCommand
和CommandHandler
创建Get
个实例,您还可以获得Save
。