Scala 如何使用Kleisli实现缓存

Scala 如何使用Kleisli实现缓存,scala,functional-programming,scalaz,kleisli,Scala,Functional Programming,Scalaz,Kleisli,我遵循了《功能和反应式建模》一书中的设计原则 因此,所有服务方法都返回Kleisli 问题是如何在这些服务上添加可更新的缓存 这是我当前的实现,是否有更好的方法(现有的组合器、更实用的方法等) [更新]关于@timotyperigo的评论,我已经在存储库级别实现了缓存 class CachedTipRepository(val self:TipRepository) extends TipRepository { var cache: Seq[Tip] = Seq.empty[Tip]

我遵循了《功能和反应式建模》一书中的设计原则

因此,所有服务方法都返回
Kleisli

问题是如何在这些服务上添加可更新的缓存

这是我当前的实现,是否有更好的方法(现有的组合器、更实用的方法等)

[更新]关于@timotyperigo的评论,我已经在存储库级别实现了缓存

class CachedTipRepository(val self:TipRepository) extends TipRepository {
  var cache: Seq[Tip] = Seq.empty[Tip]

  override def all: Future[Seq[Tip]] = …

  override def replace(tips: String): Unit = …
}

我仍然对改进设计的反馈感兴趣。

Timothy完全正确:缓存是存储库(而不是服务)的实现特性。实现特性/细节不应该在合同中公开,此时,您的设计做得很好(但不是实现!)

深入研究设计问题,您会发现在Scala中如何进行依赖项注入非常有趣:

  • 构造函数注入
  • 蛋糕图案
  • 读卡器单子
  • 蛋糕模式和构造函数注入有一个相似之处:依赖项在创建时绑定。有了Reader monad(Kleisli只是在它上面提供了一个附加层),您可以延迟绑定,这将导致更高的可组合性(由于组合器)、更高的可测试性和更高的灵活性


    如果通过添加缓存功能来装饰现有的
    TipRepository
    ,则可能不需要Kleisli的好处,甚至可能使代码更难阅读。使用构造函数注入似乎是合适的,因为它是最简单的模式,可以让您“很好地”完成任务。

    只是一种想法……在我看来,缓存之类的东西实际上不是一种域行为(即,应该是您服务的一部分),而是存储库实现的一种属性。然后,您的服务将只包含执行所需行为所需的操作,但是(如果您愿意)您的应用程序可以在缓存存储库和非缓存存储库之间进行选择。在存储库实现中,您可以使用类似State monad的功能更强大的缓存方法。
    class CachedTipRepository(val self:TipRepository) extends TipRepository {
      var cache: Seq[Tip] = Seq.empty[Tip]
    
      override def all: Future[Seq[Tip]] = …
    
      override def replace(tips: String): Unit = …
    }