2012-04-20 2 views
6

Ich würde gerne das Cake Pattern zum Teilen von Teilen eines Software-Systems in Komponenten verwenden, um es komplett modular zu machen, wie in this article vorgeschlagen. Im einfachsten Fall möchte ich einige ansprechende Komponenten haben, sagen wir Logging, Config, Database, Scripts usw., die sich gegenseitig nutzen könnten. Der Code könnte so aussehenScala Cake Pattern: Teilen von großen Komponenten in separate Dateien

trait AbstractConfig { 
    def config: AbstractConfigInterface 
    trait AbstractConfigInterface { 
    def test: Unit 
    } 
} 

trait SomeConfig extends AbstractConfig { 
    this: Core => 
    def config = SomeConfigImplementation 
    object SomeConfigImplementation extends AbstractConfigInterface { 
    def test = println("conf.test method called") 
    } 
} 

trait AbstractDatabase { 
    def database: AbstractDatabaseInterface 
    trait AbstractDatabaseInterface { 
    def connect: Unit 
    } 
} 

trait SomeDatabase extends AbstractDatabase { 
    this: Core => 
    def database = SomeDatabaseImplementation 
    object SomeDatabaseImplementation extends AbstractDatabaseInterface { 
    def connect = { 
     println("connect method called") 
     core.conf.test 
    } 
    } 
} 

trait Core { 
    this: AbstractDatabase with AbstractConfig => 
    def core = CoreInterface 
    object CoreInterface { 
    def db = database 
    def conf = config 
    } 
} 

object app extends Core with SomeDatabase with SomeConfig 

object Run { 
    def main(args: Array[String]) = { 
    app.core.db.connect 
    } 
} 

Hier die Datenbank und Konfigurationskomponenten (SomeConfig und SomeDatabase Züge) ist steckbar und kann zu einigen anderen Implementierungen geändert werden, wenn überhaupt erforderlich ist. Ihre Implementierungen haben Zugriff auf das Objekt core, das sowohl Datenbank als auch Konfiguration enthält, so dass die Datenbank bei Bedarf auf die Konfiguration zugreifen kann und umgekehrt.

Also die Frage ist: Wenn jemals ein Merkmal wie SomeDatabase wird groß und passt nicht in eine einzige Datei, wie es in separate Klassen aufgeteilt mit Zugriff auf die core Objekt? Um genauer zu sein, lassen Sie uns sagen, ich brauche, um einen Code aus connect-Methode zu bewegen in SomeDatabase auf eine andere Datei:

// SomeDatabase.scala 
trait SomeDatabase extends AbstractDatabase { 
    this: Core => 
    def database = SomeDatabaseImplementation 
    object SomeDatabaseImplementation extends AbstractDatabaseInterface { 
    def connect = { 
     val obj = new SomeClass() 
    } 
    } 
} 

// SomeClass.scala in the same package 
class SomeClass { 
    core.conf.test // Does not compile - how to make it work?? 
} 

SomeClass ist Implementierungsdetails wie SomeDatabase Werke, so möchte ich natürlich nicht, um es ein machen Merkmal und mische es in die Anwendung. Gibt es eine Möglichkeit, Zugriff auf core Objekt für SomeClass bereitzustellen?


Einige verwandte Links:

  1. Dependency Injection vs Cake Pattern by Jan Machacek
  2. Real World Scala: Dependency Injection by Jonas Boner
  3. Dependency Injection in Scala: Extending the Cake Pattern by Adam Warsky
  4. Scalable Component Abstractions by Martin Odersky & Matthias Zenger

Antwort

2

Die einfachste Sache zu tun wäre, Übergeben Sie Core als Konstruktorparameter an SomeClass.

// SomeDatabase.scala 
trait SomeDatabase extends AbstractDatabase { 
    this: Core => 
    def database = SomeDatabaseImplementation 
    object SomeDatabaseImplementation extends AbstractDatabaseInterface { 
    def connect = { 
     val obj = new SomeClass(SomeDatabase.this) // pass it here 
    } 
    } 
} 

// SomeClass.scala in the same package 
class SomeClass(coreComp: Core) { // use it here 
    coreComp.core.conf.test 
} 

Interessanterweise wollte ich eigentlich nur CoreInterface oder AbstractConfigInterface passieren, aber die Tatsache, dass sie so schwer innere Typen wirklich gemacht sind.

+0

Dave, danke für die Antwort. Bis jetzt scheint es der einzig vernünftige Weg zu sein. Das einzige, was ich nicht darüber schreibe, ist die Notwendigkeit, 'coreComp.' bei jedem Aufruf irgendeiner Kernmethode einzugeben. Leider scheint es keine Option zu geben, direkt mit 'CoreInterface' zu ​​arbeiten, oder? – nab

+1

Sie können 'coreComp._' importieren, um die Eingabe zu reduzieren. – leedm777