依赖注入的抽象字段

时间:2013-10-07 03:59:30

标签: scala dependency-injection cake-pattern

在Scala中,使用以下依赖注入方法是否有任何问题。

// Define an interface
trait FileStorage {
  def readFile(filename:String):OutputStream
}

// And an implementation
class S3FileStorage extends FileStorage {
    def readFile(filename:String):OutputStream = ???
}

// Define our service as a trait with abstract fields that need to be
// injected in order to construct. All implementation details go here.
trait FileHTTPServer {
  val fileStorage:FileStorage

  def fetchFile( session:Session, filename:String ) = ???
}

现在我们连线了

// Wire up a concrete file service that we actually use in code
// No implementation details should go here, we're simply wiring up a FileHttpServerl
// An entire project could be wired up this way in a central location if desired.
object S3FileHttpServer extends FileHTTPServer {
    val fileStorage = new S3FileStorage
}

// We could also do this anonymously
val myHttpServer = new FileHttpServer {
    val fileStorage = new S3FileStorage
} 

// Or create a mocked version for testing
val mockedHttpServer = new FileHttpServer {
    val fileStorage = mock[FileStorage]
}

显然,Cake模式提供了更大的灵活性(特别是在自我类型周围),但是对于更简单的用例,它的样板更少,同时仍然提供编译时检查和干净的明确界面。

1 个答案:

答案 0 :(得分:1)

是的,这绝对是一个很好的方法。是的,有时你可以使用构造函数注入,也没有错。但是使用构造函数注入时,您必须手动传播依赖项 ,而使用cake模式,您的依赖项将通过自我类型注释自动传播 。因此,对于大型项目,构造函数注入实际上导致了比蛋糕模式更多的样板,特别是在构造站点(您创建所有对象并在它们之间设置依赖关系)。

然而,你所呈现的并不是完整的蛋糕模式。在真正的蛋糕模式中,有一个围绕业务逻辑类的附加层,即所谓的组件,而不是直接连接逻辑类而是连接组件。

trait FileStorageComponent {
  def fileStorage: FileStorage

  trait FileStorage {
    def readFile(filename: String): OutputStream
  }
}

trait S3FileStorageComponent extends FileStorageComponent {
  val fileStorage = new S3FileStorage

  class S3FileStorage extends FileStorage {
    def readFile(filename: String): OutputStream = ???
  }
}

trait FileHttpServerComponent {
  self: FileStorageComponent =>

  val fileHttpServer = new FileHttpServer

  class FileHttpServer {
    def fetchFile(session: Session, filename: String) = ???
  }
}

// Wiring

object S3FileHttpServer extends FileHttpServerComponent with S3FileStorageComponent

// Anonymous

val server = new FileHttpServerComponent with S3FileStorageComponent

// Mocking

object TestFileHttpServer extends FileHttpServerComponent with FileStorageComponent {
  val fileStorage = mock[FileStorage]
}

在这种方法中,特征定义中有更多的样板,但作为回报,您在使用场所具有更大的灵活性和非常清晰的依赖性管理。例如,以下是我的一个项目中的程序入口点的样子:

object Main
  extends MainUI
  with DefaultActorsManagerComponent
  with DefaultPreferencesAccessComponent
  with DefaultModelComponent
  with DefaultMainWindowViewComponent
  with DefaultMainWindowControllerComponent
  with MainWindowReporterComponent
  with DefaultClientActorComponent
  with DefaultResponseParserActorComponent
  with DefaultArchiverActorComponent
  with DefaultMainWindowAccessActorComponent
  with DefaultUrlParserComponent
  with DefaultListenerActorComponent
  with DefaultXmlPrettifierComponent

所有主要程序组件都在一个地方。相当整洁的IMO。