Kri*_*ala 4 dependency-injection scala robot-legs-problem
我遇到的蛋糕模式的大多数示例似乎都将依赖关系视为单例类型服务; 在组件的最终组装中,每种类型只有一个实例.在使用Cake Pattern进行依赖注入时,是否可以编写具有多个特定类型实例的配置(可能以不同方式配置)?
请考虑以下组件.通用HTTP服务:
trait HttpService { def get(query:String):String }
trait HttpServiceComponent {
val httpService:HttpService
class HttpServiceImpl(address:String) extends HttpService {
def get(query:String):String = ...
}
}
Run Code Online (Sandbox Code Playgroud)
贸易和公司服务,每个服务都依赖于HttpService,它可能是不同的实例:
trait TradeService { def lastTrade(symbol:String):String }
trait TradeServiceComponent {
this:HttpServiceComponent => // Depends on HttpService
val tradeService:TradeService
class TradeServiceImpl extends TradeService {
def lastTrade(symbol:String):String =
httpService.get("symbol=" + symbol)
}
}
trait CompanyService { def getCompanySymbols(exchange:String):String }
trait CompanyServiceComponent {
this:HttpServiceComponent => // Depends on different HttpService instance
val companyService:CompanyService
class CompanyServiceImpl extends CompanyService {
def getCompanySymbols(exchange:String):String =
httpService.get("exchange=" + exchange)
}
}
Run Code Online (Sandbox Code Playgroud)
依赖贸易和公司服务的主要应用程序组件:
trait App { def run(exchange:String):Unit }
trait AppComponent {
this:CompanyServiceComponent with TradeServiceComponent =>
val app:App
class AppImpl extends App {
def run(exchange:String) =
companyService.getCompanySymbols(exchange).split(",").foreach(sym => {
val lastTrade = tradeService.lastTrade(sym)
printf("Last trade for %s: %s".format(sym, lastTrade))
})
}
}
Run Code Online (Sandbox Code Playgroud)
是否可以连接App以使其TradeService使用指向一个地址的HttpService,并且其CompanySerivce使用指向另一个地址的不同HttpService实例?
正如你从答案中看到的那样(尤其是丹尼尔的,也是你自己的),它是可能的,但它看起来并不优雅.出现这种困难是因为当您使用Cake模式时,将所有必需的特征混合到一个对象中(使用"with"关键字),并且您不能将特征多次混合到一个实例中.这就是mixins的工作方式,而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)
}
}
Run Code Online (Sandbox Code Playgroud)
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))
}
}
Run Code Online (Sandbox Code Playgroud)
此外,您可能需要仔细检查Cake模式是否真的最适合您的需求,因为它实际上是一个复杂的模式,依赖注入只是其中的一部分.如果你只将它用于DI,我会建议你使用更简单的解决方案.我在这里写过博客.