用implicits简化复杂的代码

时间:2017-05-18 04:49:24

标签: scala

我正在编写一段代码,我觉得这些代码变得非常复杂。

我有一个接受参数的API,这是一个特征。这种特性可以通过多种类型实现。此外,这些类中的每一个都需要由专门的处理器处理。

例如,我在下面创建了一个名为Context的Trait,它有两个实际类型的MobileContext和WebContext。

让我们说MobileContext和WebContext的记录方式不同,我们有ContextWriter [MobileContext]和ContextWriter [WebContext]形式的专门实现。

要求是该方法应该是通用的,但它应该能够根据特征的实际类型将调用分派给正确的ContextWriter。

这是我的代码。

trait Context
case class WebContext(name: String) extends Context
case class MobileContext(name: String) extends Context


trait ContextWriter[T] {
   def log(message: String, context: T) : Unit
}

object ContextWriterUtil {
   def log[T](message: String, context: T)(implicit writer: ContextWriter[T]) = {
      writer.log(message, context)
   }
}

object ContextWriterImplicits {
   implicit val webImpl = new ContextWriter[WebContext] {
      override def log(message: String, context: WebContext) = println(s"I am in web context ${context} and the message is ${message}")
   }
   implicit val mobileImpl = new ContextWriter[MobileContext] {
      override def log(message: String, context: MobileContext) = println(s"I am in mobile context ${context} and the message is ${message}")
   }
   implicit val baseImpl = new ContextWriter[Context] {
      override def log(message: String, context: Context) = context match {
         case s: WebContext => {
            val writer = implicitly[ContextWriter[WebContext]]
            writer.log(message, s)
         }
         case s: MobileContext => {
            val writer = implicitly[ContextWriter[MobileContext]]
            writer.log(message, s)
         }
         case _ => throw new Exception("don't understand this type")
      }
   }
}

import ContextWriterImplicits._
object MyApplication extends App {

   // this is the generic method.
   def call[T <: Context](message: String)(implicit context: T) = {
      val actualContext = implicitly[Context]
      ContextWriterUtil.log(message, actualContext)
   }
   def web() = {
      implicit val webContext = WebContext("web")
      call("I am calling the method")
   }
   def mobile() = {
      implicit val mobileContext = MobileContext("mobile")
      call("I am calling the method")
   }
   web()
   mobile()
}

这很有效。但我觉得它太冗长和笨拙。我想以更清洁的方式写这篇文章。

1 个答案:

答案 0 :(得分:1)

TLDR:从代码中删除继承。

我不明白为什么你需要baseImpl: ContextWriter[Context],只是删除这个隐含的,并且总是要求更精确的上下文。 call成为:

def call[T: ContextWriter](message: String)(implicit context: T) = {
   ContextWriterUtil.log(message, context)
}

要使其正常工作,您需要更新webmobile以明确指定类型参数。即使这个类型参数的单个实例化使代码编译,scalac也无法解决这个问题:

 def web() = {
    implicit val webContext = WebContext("web")
    call[WebContext]("I am calling the method")
 }

 def mobile() = {
    implicit val mobileContext = MobileContext("mobile")
    call[MobileContext]("I am calling the method")
 }

通过将ContextContextWriter合并为一个隐式输入,您可以获得明确的输入。例如,为什么不在实例化ContextWriter时使用“Context参数,并完成它?