2012-04-20 125 views
5

所以说我在我的应用程序中有两个依赖关系,一个pub pub子系统的连接和一个到数据库的连接。我可以这样做斯卡拉蛋糕模式 - 我可以有多层蛋糕?

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

和一切都很好,好世界。

但是如果我想要连接到在同一应用程序中共享相同数据库实现的两个pub子系统呢?

我要像做

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

其中蛋糕的第二层中的对象(隐式?)从母公司层面的DB实现啜。

但Scala编译器告诉我

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

蛋糕热量高,血糖负荷高...你有没有看过蛋糕中的成分列表?喜欢简单的天然食物。也就是说,最多只有一层蛋糕,并且使用def来指出所需的任何资源,否则这些资源都需要结块。(这是一个评论,因为它实际上不是解决问题的方法,只是一个建议它会变得笨拙。)删除'扩展DB_Base'并添加'{dbbase =>'并且用'DB_Layer'删除'并添加'def dbLayer = dbbase'或者其他一些代码就可以得到一个明确但紧凑的选择。 – 2012-04-20 17:21:02

+0

感谢雷克斯。我对你的评论感到困惑。首先,'{dbbase =>'完全是做什么的?我不熟悉那种语法。第二,结果看起来不像“Awesome3”吗? – dvmlls 2012-04-20 18:11:50

+0

@RexKerr是对的,但我没有看到你如何在没有附加图层的情况下滚动你自己的Cake DI(因为OP似乎在做)。例如,假设你想烘烤一些DAO蛋糕。您将需要处理数据库连接(生产,阶段,测试),连接类型(JNDI,JDBC)以及用于实现DAO的DAO合同。正如我们可以看到的OP所呈现的那样,模式变得复杂,导致很多头部划伤;-)再次,不确定如何在Scala中做蛋糕DI,而不会掉入兔子洞 – virtualeyes 2012-04-20 18:26:46

回答

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。但是如果FooBar在不同的级别上实现呢?如果我们改为

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

现在你可以混合和匹配从任何地方的任何功能;唯一的缺点是你必须命名它。(在这里,我们已经创建了一个嵌套的特质BippyImpl为了打出公用部分的属于woozles,但我们仅仅直接做到这一点。)

而且,你没有得到原来的方法不一的;你必须写代理或引用一个成员变量。

它错过了蛋糕图案的一些不错的方面,但根据我的经验,它最终比蛋糕图层大得多。现在你可以看到你可以随心所欲地嵌入它,并根据需要填写你想要的细节。

+0

我遇到了麻烦,因为另一层斯卡拉蛋糕与团结儿童容器不同。但那是我的负担。感谢您抽出时间来解释,雷克斯。欣赏它。 – dvmlls 2012-04-26 04:17:29