Comment faire une injection de dépendance avec le motif Cake sans codage dur?

j'ai juste lu et apprécié le modèle de Gâteau article . Cependant, à mon avis, l'une des principales raisons d'utiliser l'injection de dépendances est que vous pouvez faire varier les composants utilisés par un fichier XML ou des arguments en ligne de commande.

comment cet aspect de DI est-il géré avec le motif de gâteau? Les exemples que j'ai vus impliquent tous le mélange des traits statiquement.

71
demandé sur mattinbits 2011-03-02 22:12:55

5 réponses

puisque le mélange dans les traits est fait statiquement dans Scala, si vous voulez faire varier les traits mélangés à un objet, créez des objets différents basés sur une certaine condition.

prenons un exemple canonique de motif de gâteau. Vos modules sont définis comme des traits, et votre application est construite comme un objet simple avec un tas de fonctionnalités mélangées dans

val application =
    new Object
extends Communications
   with Parsing
   with Persistence
   with Logging
   with ProductionDataSource
application.startup

maintenant tous ces modules ont de belles déclarations d'auto-type qui définissent leur les dépendances inter-modules, de sorte que cette ligne ne compile que si toutes vos dépendances inter-modules existent, sont uniques et bien dactylographiées. En particulier, le module de persistance a un self-type qui dit que tout ce qui met en œuvre la persistance doit également mettre en œuvre DataSource, un module abstrait trait. Puisque ProductionDataSource hérite de DataSource, tout est génial, et cette ligne de construction d'applications compile.

mais que faire si vous voulez utiliser une source de données différente, pointer sur une base de données locale à des fins de test? Supposons en outre que vous ne pouvez pas simplement réutiliser ProductionDataSource avec différents paramètres de configuration, chargés à partir d'un fichier de propriétés. Ce que vous feriez dans ce cas est de définir un nouveau trait TestDataSource qui étend DataSource, et de le mélanger à la place. Vous pouvez même le faire dynamiquement basé sur un drapeau de ligne de commande.

val application = if (test)
  new Object
    extends Communications
      with Parsing
      with Persistence
      with Logging
      with TestDataSource
else
  new Object
    extends Communications
      with Parsing
      with Persistence
      with Logging
      with ProductionDataSource

application.startup

maintenant qui semble un peu plus verbeux que nous le voudrions, en particulier si votre l'application doit varier sa construction sur plusieurs axes. Du côté positif, vous n'avez habituellement qu'un seul morceau de logique de construction conditionnelle comme celle d'une application (ou au pire une fois par cycle de vie du composant identifiable), donc au moins la douleur est minimisée et délimitée du reste de votre logique.

55
répondu Dave Griffith 2016-10-27 00:59:23

Scala est aussi une langue de script. Votre configuration XML peut donc être un script Scala. Il est de type-sûr et pas-un-différent-langue.

il suffit de regarder le démarrage:

scala -cp first.jar:second.jar startupScript.scala

n'est pas si différent que:

java -cp first.jar:second.jar com.example.MyMainClass context.xml

Vous pouvez toujours utiliser DI, mais vous avez un outil de plus.

29
répondu shellholic 2011-03-02 19:42:49

la réponse courte est que Scala n'a actuellement aucun support intégré pour les mixins dynamiques.

je travaille sur le plugin autoproxy pour supporter cela, bien qu'il soit actuellement en attente jusqu'à la version 2.9, quand le compilateur aura de nouvelles fonctionnalités qui le rendront beaucoup plus facile.

dans l'intervalle, la meilleure façon d'atteindre presque exactement la même fonctionnalité est en mettant en œuvre votre comportement dynamiquement ajouté comme une classe de wrapper, puis ajout d'une conversion implicite à l'élément enveloppé.

5
répondu Kevin Wright 2011-03-02 22:59:47

Jusqu'à ce que le plugin AutoProxy soit disponible, une façon d'obtenir l'effet est d'utiliser la délégation:

trait Module {
  def foo: Int
}

trait DelegatedModule extends Module {
  var delegate: Module = _
  def foo = delegate.foo
}

class Impl extends Module {
  def foo = 1
}

// later
val composed: Module with ... with ... = new DelegatedModule with ... with ...
composed.delegate = choose() // choose is linear in the number of `Module` implementations

mais attention, l'inconvénient de ceci est qu'il est plus verbeux, et vous devez faire attention à l'ordre d'initialisation si vous utilisez var s à l'intérieur d'un trait. Un autre inconvénient est que s'il y a des types dépendant du chemin dans Module ci-dessus, vous ne pourrez pas utiliser la délégation aussi facilement.

mais s'il y a grand nombre d'implémentations différentes qui peuvent être variées, il vous coûtera probablement moins de code que la liste des cas avec toutes les combinaisons possibles.

3
répondu axel22 2011-03-03 14:11:59

Ascenseur a quelque chose le long de ces lignes construites. C'est surtout en code scala, mais vous avez un peu de contrôle d'exécution. http://www.assembla.com/wiki/show/liftweb/Dependency_Injection

0
répondu sblundy 2011-03-02 19:27:17