2012-04-20 4 views
5

так сказать, у меня есть две зависимости в моем приложении, подключение к некоторой подсистеме pub и подключение к базе данных. Я могу сделать что-то вродеscala take pattern - могу ли я иметь несколько слоев торта?

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

где объекты во втором слое пирога (неявно?) Чавкать в реализации БД от родительского уровня.

Но Скала компилятор говорит мне

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, чтобы указать на любые ресурсы, которые вам нужны, которые в противном случае нуждались в том, чтобы их запечатали. (Это комментарий, потому что на самом деле это не решение вашей проблемы, а просто предложение о том, что он будет громоздким.) Отбрасывание 'extends DB_Base' и добавление' {dbbase => 'и удаление' с помощью DB_Layer' и добавление 'def dbLayer = dbbase' или somesuch дает явную, но компактную альтернативу. –

+0

спасибо rex. Я смущен вашим комментарием. во-первых, что делает '{dbbase =>' точно? Я не знаком с этим синтаксисом. и во-вторых, не будет ли результат выглядеть ужасно много, как 'Awesome3'? – dvmlls

+0

@RexKerr прав, но я не вижу, как вы можете качать свой собственный торт DI (как представляется, делает OP) без дополнительных слоев. Например, скажем, вы хотите испечь какой-то торт DAO. Вам потребуется обработать соединение с базой данных (производство, этап, тест), тип соединения (JNDI, JDBC) и сам договор DAO для внедрения DAO. Как мы можем видеть, что представляет собой ОП, картина становится сложной, что приводит к большой царапине головы ;-) Опять же, не уверен, как вы можете делать Cake DI в Scala, не опуская кроличью нору – 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) 
    } 
} 

Теперь вы можете смешивать и сопоставлять любую функциональность из любого места; Единственный недостаток - это назвать его.(Здесь мы создали вложенный признак BippyImpl, чтобы разделить общие части для Woozles, но мы могли бы просто сделать это напрямую.)

Кроме того, вы не получаете смешанные исходные имена методов; вам придется писать прокси или ссылаться на переменную-член.

Он пропускает некоторые из приятных аспектов шаблона пирога, но по моему опыту он становится намного яснее, чем массивный беспорядок слоев торта. И теперь вы можете видеть, что вы можете вложить его так глубоко, как вам нравится, и заполнить нужную вам информацию, где вам нужно.

+0

У меня возникли проблемы с пониманием того факта, что еще один слой торта scala - это не то же самое, что контейнер для единства. но это мое бремя. спасибо, что нашли время, чтобы объяснить, rex. ценить это. – dvmlls

Смежные вопросы