蛋糕模式:每个实现一个组件,或每个特性一个组件?

时间:2013-01-26 00:16:38

标签: scala cake-pattern

我目前正在申请使用蛋糕模式。

关于我在网上找到的例子,这些例子是基本的,但不涉及更复杂的需求。我想做的事情并不那么花哨:我想在蛋糕模式应用程序中使用相同类型的2个服务,使用不同的实现。

trait UserServiceComponent {
  self: UserRepositoryComponent =>
  val userService: UserService

  class DefaultUserService extends UserService {
    def getPublicProfile(id: String): Either[Error, User] = userRepository.getPublicProfile(id)
  }

  class AlternativeUserService extends UserService {
    def getPublicProfile(id: String): Either[Error, User] = call webservice here for exemple...
  }
}

trait UserService extends RepositoryDelegator[User] {
  def getPublicProfile(id: String): Either[Error, User]
}

如果我一次使用UserService的一个实现,它工作正常,但如果我同时需要两个实现,我真的不知道该怎么做。

我应该创建2个不同的组件吗?每一个都暴露出不同的userService值名称? (defaultUserService / alternativeUserService)。使用一个组件进行实现我不知道其他组件在使用名称userService时如何能够知道使用了哪个实现,因为我的应用程序中有2个不同的实现。

顺便说一句,由于组件表达了对UserRepositoryComponent的依赖关系,而所有实现都不需要它,我发现只有一个组件正确有点奇怪吗? 想象一下,我不想构建需要两种实现的完整应用程序,但是对于测试来说,我只需要构建不需要UserRepositoryComponent的AlternativeUserService,否则必须提供这种依赖性是很奇怪的。因为它不会被使用。

有人可以给我一些建议,以便我知道该怎么做吗?

有关的问题: Cake pattern: how to get all objects of type UserService provided by components

由于

1 个答案:

答案 0 :(得分:8)

首先,您应该将UserServiceComponentUserService的实施分开:

trait UserService extends RepositoryDelegator[User] {
  def getPublicProfile(id: String): Either[Error, User]
}

trait UserServiceComponent {
  val userService: UserService
}

trait DefaultUserServiceComponent extends UserServiceComponent { self: UserRepositoryComponent =>
  protected class DefaultUserService extends UserService {
    def getPublicProfile(id: String): Either[Error, User] = userRepository.getPublicProfile(id)
  }
  val userService: UserService = new DefaultUserService
}

trait AlternativeUserServiceComponent extends UserServiceComponent {
  protected class AlternativeUserService extends UserService {
    def getPublicProfile(id: String): Either[Error, User] = call webservice here for exemple...
  }
  val userService: UserService = new AlternativeUserService
}

如果看起来很冗长,那就好了。蛋糕图案不是特别简洁。

但请注意,即使没有实际需要(例如仅使用UserRepositoryComponent时),它如何解决关于依赖AlternativeUserService的问题。

现在,在实例化应用程序时我们要做的就是混合使用DefaultUserServiceComponentAlternativeUserServiceComponent

如果您碰巧需要访问这两个实现,那么您确实应该公开两个userService值名称。其实,3个名字,如:

  • DefaultUserService实施的默认用户服务
  • AlternativeUserService实施
  • 的alternativeUserService
  • 任何UserService实现的mainUserService(应用程序在“混合时间”选择哪一个)。

以示例:

trait UserService extends RepositoryDelegator[User] {
  def getPublicProfile(id: String): Either[Error, User]
}

trait MainUserServiceComponent {
  val mainUserService: UserService
}

trait DefaultUserServiceComponent { self: UserRepositoryComponent =>
  protected class DefaultUserService extends UserService {
    def getPublicProfile(id: String): Either[Error, User] = userRepository.getPublicProfile(id)
  }
  val defaultUserService: UserService = new DefaultUserService
}

trait AlternativeUserServiceComponent {
  protected class AlternativeUserService extends UserService {
    def getPublicProfile(id: String): Either[Error, User] = ??? // call webservice here for exemple...
  }
  val alternativeUserService: UserService = new AlternativeUserService
}

然后你可以像这样实例化你的蛋糕:

object MyApp 
  extends MainUserServiceComponent 
  with DefaultUserServiceComponent 
  with AlternativeUserServiceComponent 
  with MyUserRepositoryComponent // Replace with your real UserRepositoryComponent here    
{
  //val userService = defaultUserService
  val mainUserService = alternativeUserService
}

在上面的示例中,明确要访问DefaultUserService的服务会将DefaultUserServiceComponent作为其组件的依赖(AlternativeUserServiceAlternativeUserServiceComponent相同),以及只需要某些 UserService的服务会将MainUserServiceComponent作为依赖项。您决定“混合时间”服务mainUserService指向哪个(此处,它指向DefaultUserService实施。