蛋糕模式在scala中的重要性

importance of cake pattern in scala

我已经开始学习 scala 一段时间了,现在正在研究蛋糕模式。我从 here

得到了例子
trait UserRepositoryComponent {
  def userLocator: UserLocator

  trait UserLocator {
    def findAll: List[User]
  }
}

trait UserRepositoryJPAComponent extends UserRepositoryComponent {
  val em: EntityManager

  def userLocator = new UserLocatorJPA(em)

  class UserLocatorJPA(val em: EntityManager) extends UserLocator {
    def findAll = {
      println("Executing a JPA query")
      List(new User, new User)
    }
  }
}

trait UserServiceComponent {
  def userService: UserService

  trait UserService {
    def findAll: List[User]
  }
}

trait DefaultUserServiceComponent extends UserServiceComponent {
  this: UserRepositoryComponent =>

  def userService = new DefaultUserService

  class DefaultUserService extends UserService {
    def findAll = userLocator.findAll
  }
}

对我来说,样板代码太多,无法将 JPA 存储库注入服务。

然而,这段代码用更少的行数就能完成同样的工作

trait UserRepository {
  def findAll
}

trait JPAUserRepository extends UserRepository {
  val em: EntityManager
  def findAll = {
    em.createQuery
    println("find using JPA")
  }
}

trait MyService {
  def findAll
}

trait MyDefaultService extends MyService {
  this: UserRepository=>
}

实例化这两个场景。

val t1 = new DefaultUserServiceComponent with UserRepositoryJPAComponent {
  val em = new EntityManager()
}
t1.userService.findAll


val t2 = new MyDefaultService with JPAUserRepository {
  val em = new EntityManager
}

t2.findAll

第二种情况使用更少的代码,并使用 DI。你能帮我了解蛋糕模式带来的额外优势吗?

与 IoC 类型的代码注入系统相比,蛋糕模式为您提供的是,在编译时,您对将要使用的实现具有明确的依赖性,而不是涉及针对运行时检查的设置一堆 XML 文件或注释。也就是说,区别是编译时 vs 运行时。

在测试中,您可以放入模拟 impl 并将它们混入。在生产中,您可以使用 "real" impl 并将它们混入。当您做错了什么时,编译器会告诉您。

(实际情况要复杂得多,因为如果混合和匹配静态对象,您可能会遇到空指针问题和各种不确定性。)

据我了解,没有太大区别。其实蛋糕图案是IoC。这只是实现 IoCDI 的想法,没有单独的 DI 框架,而只是使用 scala 代码。除非您需要更多功能,否则您可能更喜欢它而不是单独的 DI 容器。

而且在我看来你的两个例子都是蛋糕图案。至少我是这么理解的。但是 Martin 没有在他的书中将其命名为 "cake pattern",而且我对 scala mos 的了解主要基于一本书,所以我可能会遗漏一些东西。我的理解是,蛋糕模式是结合不同特征的想法,以实现 DI

我记得Martin在他的书中特别提到,在scala中使用DI-container如Spring是可以的,可惜我找不到这个地方

更新

找到它:http://www.artima.com/pins1ed/modular-programming-using-objects.html 请参阅 27.1 The problem 的最后一段。但正如我所说,他在这里不是在谈论 "cakes",尽管这个想法从你给的文章中看起来是一样的

更新 2

我刚刚重读了我的回答,我明白我需要改进它,因为它没有完全回答问题。

你应该更喜欢"cake pattern",因为它更简单。如果你用了Spring,你要维护配置,不管是XML还是注解,你可能对你的类也有一些要求(我没用过Spring ,所以我不确定是否有),而且你必须随身携带整个Spring。使用蛋糕模式,您只需编写尽可能简单的代码(您的第二个示例很简单,您应该同意)。 scala 的好处在于你可以用它做很多事情,并且只使用几个框架 - 如果你将它与 java 进行比较 - 你通常会使用更多的外部库

如果您需要更高级的功能,例如代理 - 您可以切换到 Spring,或者继续使用 Scala 并使用语言本身解决您的问题,希望 Scala 非常强大并且应该涵盖更复杂的情况.

您提供的两个代码片段之间的区别只是抽象:第一个代码片段对存储库和服务中定义的操作有更多的抽象,这些不是模式的一部分。我觉得这不是必需的,但作者决定这样展示。

在第二个示例中,您只需使用 JPAUserRepositoryfindAll 实现。但是,基本上,我认为第二种方法的问题是你通过不应该公开的业务接口公开 api(又名 UserRepositor api 在使用对象时不应该公开Service 类型 t2)

的确,蛋糕模式引入的代码比您使用某些 IoC 框架编写的代码要多一些。但是你也可以用稍微不同的方式来构造你的代码。例如,编写组件特征不是针对某个服务,而是针对逻辑上相关的一组服务。例如,所有类型的存储库服务可能驻留在 RespositoryComponent 中,而所有类型的业务服务可能驻留在 BusinessLogicComponent 中)。与 spring 进行比较,其思想在于组件实现特性与 bean 的 XML decalration 相同。

要在 Scala 中像 DI 一样使用 spring 我建议你看看 MacWire