2016-04-06 9 views
5

के साथ कैशिंग को कार्यान्वित करने के लिए मैंने फ़ंक्शनल और प्रतिक्रियाशील मॉडलिंग पुस्तक से डिज़ाइन सिद्धांत का पालन किया है।क्लेस्ली

तो सभी सेवा विधियां 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 = … 
} 

पर कैशिंग को लागू किया है मैं अभी भी डिजाइन में सुधार करने के प्रतिक्रिया के लिए इच्छुक हूँ।

+1

बस एक विचार ... ऐसा लगता है कि वास्तव में कैशिंग जैसी कुछ डोमेन व्यवहार नहीं है (यानी, कुछ ऐसा जो चाहिए अपनी सेवा का हिस्सा बनें), लेकिन शायद रिपोजिटरी कार्यान्वयन की एक संपत्ति। आपकी सेवा में केवल आवश्यक व्यवहार करने के लिए आवश्यक क्रियाएं होंगी, लेकिन (यदि आप चाहें) तो आपका आवेदन कैशिंग और गैर-कैशिंग रेपॉजिटरीज़ के बीच चयन कर सकता है। भंडार कार्यान्वयन के भीतर, आप कैशिंग के लिए एक अधिक कार्यात्मक दृष्टिकोण के लिए राज्य मोनैड की तरह कुछ उपयोग कर सकते हैं। –

उत्तर

1

तीमुथियुस पूरी तरह से सही है: कैशिंग भंडार की एक कार्यान्वयन सुविधा है (और सेवा की नहीं)। कार्यान्वयन सुविधाओं/विवरणों को अनुबंधों में प्रकट नहीं किया जाना चाहिए और इस बिंदु पर आप अपने डिजाइन के साथ अच्छा प्रदर्शन कर रहे हैं (हालांकि आपके कार्यान्वयन के साथ नहीं!)

आपकी डिज़ाइन समस्या में थोड़ा गहराई से खोना, यह देखना दिलचस्प है कि आप देखना चाहते हैं कैसे निर्भरता इंजेक्शन स्काला में किया जा सकता:

  1. निर्माता इंजेक्शन
  2. केक पैटर्न
  3. रीडर इकाई

केक पैटर्न और कन्स्ट्रक्टर इंजेक्शन में एक समानता है: निर्भरता निर्माण समय पर बंधी हुई है। रीडर इकाई के साथ आप देरी बाध्यकारी जो (combinators के कारण) अधिक composability में परिणाम है, और अधिक testability और अधिक लचीलापन

सजा के मामले में एक से मौजूदा TipRepository (Kleisli बस के ऊपर एक अतिरिक्त परत प्रदान करता है) कैशिंग कार्यक्षमता जोड़ने, क्लेस्ली के लाभों की शायद आवश्यकता नहीं होगी और वे कोड को पढ़ने के लिए कठिन बना सकते हैं। कन्स्ट्रक्टर इंजेक्शन का उपयोग करना उचित लगता है, क्योंकि यह सबसे आसान पैटर्न है जो आपको "अच्छी तरह से"