正如您从答案中看到的那样(尤其是丹尼尔的答案,还有您自己的答案),这是可能的,但看起来并不优雅。之所以出现困难,是因为当您使用 Cake 模式时,您将所有必需的特征混合到一个对象中(使用“with”关键字),并且您不能将一种特征多次混合到一个实例中。这就是 mixin 的工作原理,而 Cake 就是基于它们的。
您可以强制 Cake 处理非单例依赖项的事实并不意味着您应该这样做。我建议您在这种情况下简单地使用普通的构造函数,这就是自我类型注释不太适合的地方:
trait HttpService { ... }
/* HttpServiceImpl has become a top-level class now,
* as the Cake pattern adds no more value here.
* In addition, trait HttpServiceComponent gets deleted */
class HttpServiceImpl(address:String) extends HttpService {
...
}
trait TradeService { def lastTrade(symbol:String):String }
trait TradeServiceComponent {
// The dependency on HttpService is no longer declared as self-type
val tradeService:TradeService
// It is declared as a constructor parameter now
class TradeServiceImpl(httpService: HttpService) extends TradeService {
def lastTrade(symbol:String):String =
httpService.get("symbol=" + symbol)
}
}
trait CompanyService { def getCompanySymbols(exchange:String):String }
trait CompanyServiceComponent {
// Again, self-type annotation deleted
val companyService:CompanyService
// Again, the dependency is declared as a constructor parameter
class CompanyServiceImpl(httpService: HttpService) extends CompanyService {
def getCompanySymbols(exchange:String):String =
httpService.get("exchange=" + exchange)
}
}
App 和 AppComponent 特征保持其原始形式。现在您可以通过以下方式使用所有组件:
object App {
def main(args:Array[String]):Unit = {
val appAssembly = new AppComponent
with TradeServiceComponent
with CompanyServiceComponent {
// Note, that HttpServiceComponent it neither needed nor mixed-in now
val tradeService = new TradeServiceImpl(
new HttpServiceImpl("http://trades-r-us.com"))
val companyService = new CompanyServiceImpl(
new HttpServiceImpl("http://exchange-services.com"))
val app = new AppImpl
}
appAssembly.app.run(args(0))
}
}
另外,您可能需要仔细检查 Cake 模式是否真的最适合您的需求,因为它实际上是一种复杂的模式,依赖注入只是其中的一部分。如果您仅将其用于 DI,我建议您使用更简单的解决方案。我已经在博客上谈到了这一点here http://biased-and-fair-software-development.blogspot.com/2011/01/slicing-cake.html.