Scala 蛋糕图案 : Splitting large components into separate files

标签 scala cake-pattern

我想使用蛋糕模式将某些软件系统的各个部分拆分为组件,以使其完全模块化,如 this article 中提出的那样。 。在最简单的情况下,我想要一些可模拟的组件,比如说日志记录、配置、数据库、脚本等,它们可能会相互使用。代码可能看起来像

trait AbstractConfig {
  def config: AbstractConfigInterface
  trait AbstractConfigInterface {
    def test: Unit
  }
}

trait SomeConfig extends AbstractConfig {
  this: Core =>  
  def config = SomeConfigImplementation
  object SomeConfigImplementation extends AbstractConfigInterface {
    def test = println("conf.test method called")
  }
}

trait AbstractDatabase {
  def database: AbstractDatabaseInterface
  trait AbstractDatabaseInterface {
    def connect: Unit
  }
}

trait SomeDatabase extends AbstractDatabase {
  this: Core =>
  def database = SomeDatabaseImplementation
  object SomeDatabaseImplementation extends AbstractDatabaseInterface {
    def connect = {
      println("connect method called")
      core.conf.test
    }
  }
}

trait Core {
  this: AbstractDatabase with AbstractConfig =>
  def core = CoreInterface
  object CoreInterface {
    def db = database
    def conf = config
  }
}

object app extends Core with SomeDatabase with SomeConfig

object Run {
  def main(args: Array[String]) = {
    app.core.db.connect
  }
}

这里的数据库和配置组件(SomeConfigSomeDatabase 特征)是可插入的,并且可以根据需要更改为其他一些实现。他们的实现可以访问包含数据库和配置的core对象,因此数据库可以在需要时访问配置,反之亦然。

所以问题是:如果像 SomeDatabase 这样的特征变得很大并且不适合单个文件,如何将其拆分为单独的类来保留对 core 的访问权限> 对象?更具体地说,假设我需要将 SomeDatabase 中的 connect 方法中的一些代码移至另一个文件:

// SomeDatabase.scala
trait SomeDatabase extends AbstractDatabase {
  this: Core =>
  def database = SomeDatabaseImplementation
  object SomeDatabaseImplementation extends AbstractDatabaseInterface {
    def connect = {
      val obj = new SomeClass()
    }
  }
}

// SomeClass.scala in the same package
class SomeClass {
    core.conf.test // Does not compile - how to make it work??
}

SomeClass 是 SomeDatabase 如何工作的实现细节,所以我显然不想将其作为一个特征并将其混合到应用程序中。有没有办法为 SomeClass 提供对 core 对象的访问?

<小时/>

一些相关链接:

  1. Dependency Injection vs Cake Pattern by Jan Machacek
  2. Real World Scala: Dependency Injection by Jonas Boner
  3. Dependency Injection in Scala: Extending the Cake Pattern by Adam Warsky
  4. Scalable Component Abstractions by Martin Odersky & Matthias Zenger

最佳答案

最简单的做法是将 Core 作为构造函数参数传递给 SomeClass

// SomeDatabase.scala
trait SomeDatabase extends AbstractDatabase {
  this: Core =>
  def database = SomeDatabaseImplementation
  object SomeDatabaseImplementation extends AbstractDatabaseInterface {
    def connect = {
      val obj = new SomeClass(SomeDatabase.this) // pass it here
    }
  }
}

// SomeClass.scala in the same package
class SomeClass(coreComp: Core) { // use it here
    coreComp.core.conf.test
}

有趣的是,我真的只是想传递 CoreInterfaceAbstractConfigInterface,但它们是内部类型这一事实确实让这变得很困难。

关于Scala 蛋糕图案 : Splitting large components into separate files,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10251892/

相关文章:

java - 适用于 Android 的 Scala 库

macos - sbt - Scala - Brew - MacOS - 根本无法连接到 sbt shell : NoClassDefFoundError

scala - 蛋糕图案 : mixing in in a trait

scala - 使用成员访问而不是提取器时奇怪的类型不匹配

java - 在没有特征对象的 Scala 中重用 Java 泛型集合

scala - Play Framework 路由中的非捕获通配符

scala - 蛋糕图案和类型

Akka 和蛋糕图案

scala - 避免名称与 Cake Pattern 冲突

Scaladoc 无法为方法和类签名中的内部类生成链接