2012-04-20 13 views
5

तो कहें कि मेरे पास मेरे ऐप में दो निर्भरताएं हैं, कुछ पब उप सिस्टम से कनेक्शन है, और डेटाबेस से कनेक्शन है। मैं की तरहस्कैला केक पैटर्न - क्या मेरे पास केक की कई परतें हो सकती हैं?

trait DB { 
    def lookup(query:String):String 
} 

trait PubSub { 
    def subscribe(key:String, callback:String => Any) 
} 

कुछ करना तो मैं जैसे

trait Functionality { this:DB with PubSub => 
    def doSomething() { 
     val key = lookup("get key") 
     subscribe(key, data => println(data)) 
    } 
} 

और फिर मेरा तर्क लिख सकते हैं मेरे ऐप की तरह

object Awesome extends App { 

    object repository extends Functionality with DB with PubSub { 
     def lookup(query:String) = "some key" 
     def subscribe(key:String, callback:String => Any) { 
      scala.concurrent.ops.spawn { while(true) { callback(key) ; Thread.Sleep(1000) } } 
     } 
    } 
    repository.doSomething() 
} 

हो सकता है और सब कुछ ठीक है और अच्छा दुनिया में है सकता है।

लेकिन अगर मैं दो पब सब सिस्टम से कनेक्शन चाहता हूं जो एक ही ऐप में समान डेटाबेस कार्यान्वयन साझा करते हैं?

मैं की तरह

object Awesome2 extends App { 
    object repository extends DB { 
     def lookup(query: String): String = "some other key" 

     object connection1 extends Functionality with PubSub with DB { 
      def subscribe(key: String, callback: (String) => Any) { 
       scala.concurrent.ops.spawn { while(true) { callback(key.toUpperCase) ; Thread.sleep(1000) } } 
      } 
     } 

     object connection2 extends Functionality with PubSub with DB { 
      def subscribe(key: String, callback: (String) => Any) { 
       scala.concurrent.ops.spawn { while(true) { callback(key.toLowerCase) ; Thread.sleep(1000) } } 
      } 
     } 
    } 
} 

जहां केक की दूसरी परत में वस्तुओं (परोक्ष?) माता-पिता के स्तर से डीबी कार्यान्वयन में slurp कुछ करना चाहता हूँ।

लेकिन स्केला संकलक मुझसे कहता है

error: object creation impossible, since method lookup in trait DB of type (query:String) String is not defined 
object connection2 extends Functionality with PubSub with DB { 

अगर मैं करता हूँ उसके बाद निम्न यह क्या मैं

object Awesome3 extends App { 
    object repository extends DB { 
     override def lookup(query: String): String = "some other key" 

     object connection1 extends Functionality with PubSub with DB { 
      def subscribe(key: String, callback: (String) => Any) { 
       scala.concurrent.ops.spawn { while(true) { callback(key.toUpperCase) ; Thread.sleep(1000) } } 
      } 

      def lookup(query: String): String = repository.lookup(query) 
     } 

     object connection2 extends Functionality with PubSub with DB { 
      def subscribe(key: String, callback: (String) => Any) { 
       scala.concurrent.ops.spawn { while(true) { callback(key.toLowerCase) ; Thread.sleep(1000) } } 
      } 

      def lookup(query: String): String = repository.lookup(query) 
     } 
    } 
    repository.connection1.doSomething() 
    repository.connection2.doSomething() 
} 

चाहते करती है, लेकिन इस तरह के गन्दा

मैं इस विशेषता जोड़ सकते हैं

trait DB_Base extends DB { 

    private val db:DB = this 

    trait DB_Layer extends DB { 
     def lookup(query:String):String = db.lookup(query) 
    } 
} 

और उसके बाद निम्नलिखित काम

object Awesome4 extends App { 
    object repository extends DB_Base { 
     override def lookup(query: String): String = "some other key" 

     object connection1 extends Functionality with PubSub with DB_Layer { 
      def subscribe(key: String, callback: (String) => Any) { 
       scala.concurrent.ops.spawn { while(true) { callback(key.toUpperCase) ; Thread.sleep(1000) } } 
      } 
     } 

     object connection2 extends Functionality with PubSub with DB_Layer { 
      def subscribe(key: String, callback: (String) => Any) { 
       scala.concurrent.ops.spawn { while(true) { callback(key.toLowerCase) ; Thread.sleep(1000) } } 
      } 
     } 
    }  
    repository.connection1.doSomething() 
    repository.connection2.doSomething() 
} 

तो अब मेरे पास दो परतें हैं। मैं तीन कैसे प्राप्त करूं? मुझे लगता है कि मैं साजिश खो रहा हूँ।

+2

केक कैलोरी में उच्च है और उच्च ग्लाइसेमिक लोड है ... और क्या आपने कभी एक स्तरित केक में सामग्री की सूची देखी है? सरल, प्राकृतिक खाद्य पदार्थ पसंद करते हैं। यही है, सबसे अधिक एक परत वाला केक है, और किसी भी संसाधन को इंगित करने के लिए एक डीफ़ का उपयोग करें जिसे अन्यथा आपको जरूरी है। (यह एक टिप्पणी है क्योंकि यह वास्तव में आपकी समस्या का समाधान नहीं है, केवल एक सुझाव है कि यह अनावश्यक हो रहा है।) 'डीबीबीएएसई' को बढ़ाकर '{dbbase =>' जोड़ना और डीबी_लेयर के साथ 'ड्रॉप करना 'और' def dbLayer = dbbase' जोड़ना या कुछ स्पष्ट रूप से एक स्पष्ट अभी तक कॉम्पैक्ट विकल्प उत्पन्न करना। –

+0

धन्यवाद रेक्स। मैं आपकी टिप्पणी से उलझन में हूँ। सबसे पहले, '{dbbase =>' वास्तव में क्या करता है? मैं उस वाक्यविन्यास से परिचित नहीं हूँ। और दूसरा, क्या परिणाम 'Awesome3' की तरह एक भयानक नहीं दिखेंगे? – dvmlls

+0

@RexKerr सही है लेकिन मुझे नहीं लगता कि आप अतिरिक्त परतों के बिना अपना खुद का केक डी (जैसे ओपी प्रतीत होता है) कैसे रोल कर सकते हैं। उदाहरण के लिए, मान लें कि आप कुछ डीएओ केक सेंकना चाहते हैं। आपको डेटाबेस कनेक्शन (उत्पादन, चरण, परीक्षण), कनेक्शन प्रकार (जेएनडीआई, जेडीबीसी), और डीएओ अनुबंध को डीएओ लागू करने के लिए स्वयं को संभालने की आवश्यकता होगी। चूंकि हम ओपी प्रस्तुत करने के साथ देख सकते हैं, पैटर्न जटिल हो जाता है, जिससे सिर खरोंच हो जाता है ;-) फिर से, यह सुनिश्चित न करें कि कैसे आप खरगोश छेद नीचे जाकर स्कैला में केक डी कर सकते हैं – virtualeyes

उत्तर

4

एक टिप्पणी इसे समझाने के लिए पर्याप्त नहीं है, इसलिए यहां एक उत्तर है जो मूल रूप से कहता है, "ऐसा मत करो!" और एक विकल्प सुझाता है।

आप जिस मुख्य समस्या में भाग ले रहे हैं वह यह है कि आप कुछ कार्यक्षमताओं की कई प्रतियां रखना चाहते हैं, लेकिन आपके पास नाम (केवल प्रकार के अनुसार) से इसका संदर्भ देने का कोई तरीका नहीं है। समाधान है: इसे एक नाम दें।

चलो अपना डबल-केक पैटर्न लें।

trait Foo { def foo(s: String): String } 
trait Bar { def bar(s: String, f: String => Any): Any } 
trait Bippy { this: Foo with Bar => 
    def bip(s: String) = bar(foo(s),println) 
} 

ठीक है, महान है, हम Bippy में कुछ भी है कि Foo with Bar लागू करता है करने के लिए मिश्रण कर सकते हैं और हम bip कर सकेंगे। लेकिन क्या होगा यदि Foo और Bar विभिन्न स्तरों पर लागू किए गए हैं? यदि हम इसके बजाय

trait Bippy { 
    def myFoo: Foo 
    def myBar: Bar 
    def bip(s: String) = myBar.bar(myFoo.foo(s), println) 
} 

यह प्रारंभ में अधिक अजीब दिखता है। (यह है।) लेकिन अब यह तेजी से अजीब तरीकों से केक को मजबूर होने के बजाय मिश्रण और मिलान करने देता है। उदाहरण के लिए:

object Foozle extends Foo { theFoo => 
    def foo(s: String) = s.toUpperCase 
    trait BippyImpl extends Bippy { this: Bar => 
    def myFoo = theFoo 
    def myBar = this 
    } 
    object Woozle1 extends BippyImpl with Bar { 
    def bar(s: String, f: String => Any) = f(s) 
    } 
    object Woozle2 extends BippyImpl with Bar { 
    def bar(s: String, f: String => Any) = f(s.reverse) 
    } 
} 

अब आप मिश्रण और कहीं से भी किसी भी कार्यक्षमता से मेल कर सकते हैं; केवल नकारात्मक पक्ष आपको इसका नाम देना होगा।(यहां हमने Woozles के लिए सामान्य भागों को विभाजित करने के लिए एक नेस्टेड विशेषता BippyImpl बनाई है, लेकिन हम इसे सीधे कर सकते हैं।)

इसके अलावा, आपको मूल विधि नाम मिश्रित नहीं होते हैं; आपको प्रॉक्सी लिखना होगा या सदस्य चर का संदर्भ देना होगा।

यह केक पैटर्न के कुछ अच्छे पहलुओं से चूक जाता है, लेकिन मेरे अनुभव में यह केक परतों की भारी गड़बड़ी से बहुत स्पष्ट हो जाता है। और अब आप देख सकते हैं कि आप इसे जितना चाहें उतना गहराई से घोंसला कर सकते हैं और जहां चाहें विवरण भर सकते हैं।

+0

मुझे इस तथ्य के साथ आने में परेशानी हो रही है कि स्केल केक की एक और परत एकता बच्चे कंटेनर के समान नहीं है। लेकिन यह मेरा बोझ है। व्याख्या करने के लिए समय लेने के लिए धन्यवाद, रेक्स। इसकी प्रशंसा करना। – dvmlls

संबंधित मुद्दे