【发布时间】:2016-04-06 20:36:56
【问题描述】:
我遵循Functional and Reactive Modeling一书中的设计原则。
所以所有 service 方法都返回Kleisli。
问题是如何在这些服务上添加可更新缓存。
这是我目前的实现,有没有更好的方法(现有的组合器,更多功能的方法,......)?
import scala.concurrent.duration.Duration
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{Await, Future}
import scalaz.Kleisli
trait Repository {
def all : Future[Seq[String]]
def replaceAll(l: Seq[String]) : Future[Unit]
}
trait Service {
def all = Kleisli[Future, Repository, Seq[String]] { _.all }
def replaceAll(l: Seq[String]) = Kleisli[Future, Repository, Unit] { _.replaceAll(l) }
}
trait CacheService extends Service {
var cache : Seq[String] = Seq.empty[String]
override def all = Kleisli[Future, Repository, Seq[String]] { repo: Repository =>
if (cache.isEmpty) {
val fcache = repo.all
fcache.foreach(cache = _)
fcache
}
else
Future.successful(cache)
}
override def replaceAll(l: Seq[String]) = Kleisli[Future, Repository, Unit] { repo: Repository =>
cache = l
repo.replaceAll(l)
}
}
object CacheTest extends App {
val repo = new Repository {
override def replaceAll(l: Seq[String]): Future[Unit] = Future.successful()
override def all: Future[Seq[String]] = Future.successful(Seq("1","2","3"))
}
val service = new CacheService {}
println(Await.result(service.all(repo), Duration.Inf))
Await.result(service.replaceAll(List("a"))(repo), Duration.Inf)
println(Await.result(service.all(repo), Duration.Inf))
}
[更新] 关于@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 = …
}
我仍然对改进设计感兴趣。
【问题讨论】:
-
只是一个想法......在我看来,像缓存这样的东西真的不是域行为(即,应该是您服务的一部分),而可能是存储库的属性执行。然后,您的服务将仅包含执行所需行为所需的操作,但是(如果您愿意)您的应用程序可以在缓存和非缓存存储库之间进行选择。在存储库实现中,您可以使用 State monad 之类的东西来获得更实用的缓存方法。
标签: scala functional-programming scalaz kleisli