0

在 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 模式提供了更大的灵活性(尤其是围绕自身类型),但是对于更简单的用例,它的样板代码要少得多,同时仍然提供编译时检查和干净明确的接口。

4

1 回答 1

1

是的,这绝对是个好方法。是的,有时您可以使用构造函数注入,这也没有错。但是使用构造函数注入,您必须手动传播依赖项,而使用蛋糕模式,您的依赖项会通过自类型注释自动传播。因此,对于大型项目,构造函数注入实际上会导致比蛋糕模式更多的样板文件,尤其是在施工现场(您创建所有对象并设置它们之间的依赖关系)。

然而,你所呈现的并不是成熟的蛋糕图案。在真正的蛋糕模式中,业务逻辑类周围有一个额外的层,即所谓的组件,您不直接连接逻辑类,而是连接组件。

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]
}

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

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

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

于 2013-10-07T08:30:53.797 回答