在Scala

时间:2017-11-22 12:06:39

标签: scala typeclass implicit

我有类型类:

trait ProcessorTo[T]{
    def process(s: String): T
}

及其实施

class DefaultProcessor extends ProcessorTo[String]{
    def process(s: String): String = s
}
trait DefaultProcessorSupport{
    implicit val p: Processor[String] = new DefaultProcessor
}

使其可以使用我创建的

object ApplicationContext
    extends DefaultProcessorSupport
    with //Some other typeclasses

但现在我必须添加一个执行某些DataBase的处理器 - 读取。数据库URL等位于condifguration文件中, 仅可用于运行时 。现在我做了以下事情。

class DbProcessor extends ProcessorTo[Int]{
   private var config: Config = _
   def start(config: Config) = //set the configuration, open connections etc
   //Other implementation
}

object ApplicationContext{
    implicit val p: ProcessorTo[Int] = new DbProcessor
    def configure(config: Config) = p.asInstanceOf[DbProcessor].start(config)
}

它对我有用,但我对这种技术不太确定。对我来说有点奇怪。这是一种不好的做法吗?如果是这样,那将是一个很好的解决方案?

2 个答案:

答案 0 :(得分:1)

由于DbProcessor缺少流程实现(???)而特质ProcessorTo[T]缺少DbProcessor中定义的start方法,因此我对这些要求感到有些困惑。因此,我会在回答时假设以下内容:类型类同时具有processstart方法

定义类型类:

  trait ProcessorTo[T]{
    def start(config: Config): Unit
    def process(s: String): T
  }

在随播对象中提供类型类的实现:

object ProcessorTo {
  implicit object DbProcessor extends ProcessorTo[Int] {
    override def start(config: Config): Unit = ???
    override def process(s: String): Int = ???
  }

  implicit object DefaultProcessor extends ProcessorTo[String] {
    override def start(config: Config): Unit = ???
    override def process(s: String): String = s
  }
}

并在ApplicationContext中使用它,如下所示:

  object ApplicationContext {
    def configure[T](config: Config)(implicit ev: ProcessorTo[T]) = ev.start(config)
  }

这是一篇关于类型类的好文章:http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala-part-12-type-classes.html

答案 1 :(得分:1)

我真的不明白为什么你需要start。如果隐式DbProcessor有依赖关系,为什么不通过构造函数使它成为显式依赖?我的意思是这样的:

class DbConfig(val settings: Map[String, Object]) {}

class DbProcessor(config: DbConfig) extends ProcessorTo[Int] {

  // here goes actual configuration of the processor using config
  private val mappings: Map[String, Int] = config.settings("DbProcessor").asInstanceOf[Map[String, Int]]

  override def process(s: String): Int = mappings.getOrElse(s, -1)
}


object ApplicationContext {
  // first create config then pass it explicitly
  val config = new DbConfig(Map[String, Object]("DbProcessor" -> Map("1" -> 123)))
  implicit val p: ProcessorTo[Int] = new DbProcessor(config)
}

或者如果你喜欢Cake pattern,你可以这样做:

trait DbConfig {
  def getMappings(): Map[String, Int]
}

class DbProcessor(config: DbConfig) extends ProcessorTo[Int] {
  // here goes actual configuration of the processor using config
  private val mappings: Map[String, Int] = config.getMappings()

  override def process(s: String): Int = mappings.getOrElse(s, -1)
}

trait DbProcessorSupport {
  self: DbConfig =>
  implicit val dbProcessor: ProcessorTo[Int] = new DbProcessor(self)
}

object ApplicationContext extends DbConfig with DbProcessorSupport {
  override def getMappings(): Map[String, Int] = Map("1" -> 123)
}

因此,您在ApplicationContext中唯一能做的就是提供DbConfig特质的实际实施。