5

私のアプリには、pub サブシステムへの接続とデータベースへの接続という 2 つの依存関係があるとします。私は次のようなことができます

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

そして、すべてが世界でうまくいっています。

しかし、同じアプリで同じデータベース実装を共有する 2 つの 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) } }
            }
        }
    }
}

ケーキの第 2 層のオブジェクトは (暗黙のうちに?) 親レベルから 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つの層を持っています。どうすれば3つ手に入る?プロットを失っているような気がします。

4

1 に答える 1

5

コメントはそれを説明するのに十分な大きさではないので、基本的に「それをしないでください!」と言う答えがあります。と代替案を提案します。

あなたが直面している重要な問題は、いくつかの機能の複数のコピーを持ちたいのですが、名前で (タイプだけで) 参照する方法がないということです。解決策は次のとおりです。名前を付けます。

ダブルケーキのパターンを見てみましょう。

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 を作成しましたが、直接行うこともできます。)

また、元のメソッド名が混在することはありません。プロキシを記述するか、メンバー変数を参照する必要があります。

ケーキ パターンのいくつかの優れた側面を見逃していますが、私の経験では、大量のケーキ層の混乱よりもはるかに明確になります。これで、好きなだけ深くネストして、必要な場所に必要な詳細を入力できることがわかります。

于 2012-04-20T19:29:41.950 に答える