蛋糕模式可以用于非单例样式依赖吗?

发布于 2024-10-20 03:36:43 字数 1729 浏览 2 评论 0原文

我遇到的大多数蛋糕模式示例似乎都将依赖关系视为单例类型服务;其中,在组件的最终组装中每种类型只有一个实例。当使用蛋糕模式进行依赖注入时,是否可以编写一个具有多个特定类型实例的配置,可能以不同的方式配置?

考虑以下组件。通用 HTTP 服务:

trait HttpService { def get(query:String):String }
trait HttpServiceComponent {
  val httpService:HttpService
  class HttpServiceImpl(address:String) extends HttpService {
    def get(query:String):String = ...
  }
}

贸易和服务公司服务,每个都依赖于 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)
  }
}

依赖于 Trade & 的主应用程序组件;公司服务:

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

是否可以连接应用程序,使其 TradeService 使用指向一个地址的 HttpService,而其 CompanySerivce 使用指向另一个地址的不同 HttpService 实例?

Most of the examples of the Cake Pattern I've come across appear to consider dependencies as singleton type services; where there is only one instance of each type in the final assembly of components. Is it possible to write a configuration that has more than one instance of a particular type, perhaps configured in different ways, when using the Cake Pattern for dependency injection?

Consider the following components. Generic HTTP service:

trait HttpService { def get(query:String):String }
trait HttpServiceComponent {
  val httpService:HttpService
  class HttpServiceImpl(address:String) extends HttpService {
    def get(query:String):String = ...
  }
}

Trade & Company services, that each depend on an HttpService, which may be different instances:

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

Main app component that depends on Trade & Company services:

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

Is it possible to wire up the App so that its TradeService uses a HttpService that points to one address, and its CompanySerivce uses a different HttpService instance pointing to another address?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

半仙 2024-10-27 03:36:43

正如您从答案中看到的那样(尤其是丹尼尔的答案,还有您自己的答案),这是可能的,但看起来并不优雅。之所以出现困难,是因为当您使用 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)
  }
}

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,我建议您使用更简单的解决方案。我在博客上写过在这里

As you can see from the answers (notably Daniel's, but also your own), it is possible, but it doesn't look elegant. The difficulty appears because when you use the Cake pattern, you mix all required traits into one object (using "with" keyword), and you cannot mix a trait more than once into one instance. That is how mixins work, and the Cake is based on them.

The fact you can force Cake to handle non-singleton dependencies doesn't mean you should do it. I would advise you to simply use plain-old constructor in such cases, that is where self-type annotation doesn't fit well:

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

The App and AppComponent traits stay in their original form. Now you can use the all components in the following way:

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

Also, you may want do double-check if the Cake pattern is really best suited for your needs, as it is actually a complex pattern and dependency injection is only one part of it. If you use it only for DI, I would advise you to use a simpler solution. I've blogged about that here.

秋风の叶未落 2024-10-27 03:36:43

由于每个“客户端”可能需要不同的实现,因此您只需参数化服务即可。

trait HttpService { def get(query:String):String }
trait HttpServiceComponent {
  def httpService(name: String):HttpService
  class HttpServiceImpl(address:String) extends HttpService {
    def get(query:String):String = ...
  }
}

像这样使用:

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("TradeService").get("symbol=" + symbol)
  }
}

最终的混合将执行如下操作:

trait AppComponent {
  this:CompanyServiceComponent with TradeServiceComponent =>
  val httpServices = Map( "TradeService"   -> new HttpServiceImpl("http://trades-r-us.com"),
                          "CompanyService" -> new HttpServiceImpl("http://exchange-services.com"))
  def httpService(name: String) = httpServices(name)

Since each "client" may need a different implementation, you could just parameterize the service.

trait HttpService { def get(query:String):String }
trait HttpServiceComponent {
  def httpService(name: String):HttpService
  class HttpServiceImpl(address:String) extends HttpService {
    def get(query:String):String = ...
  }
}

To be used like this:

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("TradeService").get("symbol=" + symbol)
  }
}

The final mix would then do something like this:

trait AppComponent {
  this:CompanyServiceComponent with TradeServiceComponent =>
  val httpServices = Map( "TradeService"   -> new HttpServiceImpl("http://trades-r-us.com"),
                          "CompanyService" -> new HttpServiceImpl("http://exchange-services.com"))
  def httpService(name: String) = httpServices(name)
最后的乘客 2024-10-27 03:36:43

这可以按预期编译和运行,但还有很多不足之处:

object App {
  def main(args:Array[String]):Unit = {
    val tradeServiceAssembly = new TradeServiceComponent with HttpServiceComponent {
      val httpService = new HttpServiceImpl("http://trades-r-us.com")
      val tradeService = new TradeServiceImpl
    }
    val companyServiceAssembly = new CompanyServiceComponent with HttpServiceComponent {
      val httpService = new HttpServiceImpl("http://exchange-services.com")
      val companyService = new CompanyServiceImpl
    }
    val appAssembly = new AppComponent 
        with TradeServiceComponent
        with CompanyServiceComponent
        with HttpServiceComponent {
      lazy val httpService = error("Required for compilation but not used")
      val tradeService = tradeServiceAssembly.tradeService
      val companyService = companyServiceAssembly.companyService
      val app = new AppImpl
    }
    appAssembly.app.run(args(0))
  }
}

This compiles and runs as expected, but it leaves a lot to be desired:

object App {
  def main(args:Array[String]):Unit = {
    val tradeServiceAssembly = new TradeServiceComponent with HttpServiceComponent {
      val httpService = new HttpServiceImpl("http://trades-r-us.com")
      val tradeService = new TradeServiceImpl
    }
    val companyServiceAssembly = new CompanyServiceComponent with HttpServiceComponent {
      val httpService = new HttpServiceImpl("http://exchange-services.com")
      val companyService = new CompanyServiceImpl
    }
    val appAssembly = new AppComponent 
        with TradeServiceComponent
        with CompanyServiceComponent
        with HttpServiceComponent {
      lazy val httpService = error("Required for compilation but not used")
      val tradeService = tradeServiceAssembly.tradeService
      val companyService = companyServiceAssembly.companyService
      val app = new AppImpl
    }
    appAssembly.app.run(args(0))
  }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文