23

Je commence enfin à me mouiller les pieds avec Dependency Injection (longtemps attendu); J'ai commencé à jouer avec Unity et j'ai eu un problème avec le schéma de stratégie. Je peux utiliser le conteneur pour me rendre des implémentations spécifiques d'une stratégie basée sur un nom, mais ce que je ne vois pas, c'est comment je suis censé avoir la bonne stratégie dans le contexte. Illustrons sur un exemple simple: le contexte est une voiture, qui a un IEngine (la stratégie), avec 2 implémentations, FastEngine et SlowEngine. Le code regarderait le long de ces lignes:Modèle de stratégie et injection de dépendances en utilisant Unity

public interface IEngine 
{ 
    double MaxSpeed 
    { 
     get; 
    } 
} 

internal class FastEngine:IEngine 
{ 
    public double MaxSpeed 
    { 
     get 
     { 
      return 100d; 
     } 
    } 
} 

internal class SlowEngine:IEngine 
{ 
    public double MaxSpeed 
    { 
     get 
     { 
      return 10d; 
     } 
    } 
} 

public class Car 
{ 
    private IEngine engine; 
    public double MaximumSpeed 
    { 
     get 
     { 
      return this.engine.MaxSpeed; 
     } 
    } 

    public Car(IEngine engine) 
    { 
     this.engine = engine; 
    } 
} 

Mon problème est le suivant: comment dois-je aller sur instancier une voiture rapide ou une voiture lente? Je peux utiliser le conteneur pour me fournir chaque mise en œuvre, et je peux définir une mise en œuvre « par défaut » à utiliser:

IUnityContainer container = new UnityContainer(); 
container.RegisterType<IEngine, FastEngine>(); 
container.RegisterType<IEngine, FastEngine>("Fast"); 
container.RegisterType<IEngine, SlowEngine>("Slow"); 
var car = container.Resolve<Car>(); 
Assert.AreEqual(100, car.MaximumSpeed); 

mais ce que je voudrais est d'être en mesure de demander une voiture avec une mise en œuvre spécifique du stratégie - quelque chose comme

var car = container.Resolve<Car>(??? use "Fast" or "Slow ???); 

Puis-je utiliser le conteneur pour faire cela? Ou devrais-je écrire une usine qui utilise le conteneur? Toute orientation serait appréciée - je ne suis pas sûr que je pense à ce sujet!

Répondre

26

Un motif commun dans DI est que lors de l'exécution il ne va y avoir qu'une seule implémentation d'une abstraction donnée. Cela rend la vie beaucoup plus facile, car vous n'avez pas besoin de faire face à l'ambiguïté telle que celle que vous décrivez.

Toutefois, il est parfois nécessaire de faire varier une implémentation en fonction du contexte, comme dans l'exemple donné. De nombreux conteneurs DI fournissent des moyens de fournir un paramètre qualificatif, mais cela signifie que vous finirez par coupler étroitement votre code à un conteneur DI spécifique.

Une solution beaucoup mieux serait d'introduire un Abstract Factory qui peut fournir ce dont vous avez besoin. Quelque chose comme

public interface ICarFactory 
{ 
    Car Create(IEngine engine); 
} 

Si vous avez besoin d'injecter plus de stratégies, peut-être le modèle de conception Builder pourrait adapter encore mieux. Dans tous les cas, au lieu d'enregistrer un grand nombre de voitures différentes dans le conteneur, vous devez au contraire enregistrer une seule implémentation ICarFactory.

Dans votre code client, vous utiliseriez ICarFactory injecté pour créer une instance Car basée sur un moteur IE particulier.

var car = factory.Create(engine); 
+0

Merci, réponse perspicace. J'utilise le modèle de stratégie, avec de multiples stratégies échangées à l'exécution, beaucoup; par défaut je ferais ce que tu décrirais (Factory ou Builder), mais j'ai vu beaucoup de pattern de stratégie et de DI, et bien que cela puisse aider. D'après ce que vous dites, il semble qu'un conteneur ne serait que marginalement utile. – Mathias

+0

Je pense toujours que les conteneurs sont extrêmement utiles. Dans ces cas, ils injecteraient simplement l'usine au lieu de la stratégie, mais je suppose que vous pourriez toujours choisir d'implémenter l'usine avec le conteneur ... –

+4

Oh, je pense que je vois ce que vous voulez dire; plutôt que de retourner la bonne voiture, retournez la bonne usine en fonction du moteur. Dans tous les cas, votre commentaire sur: les conteneurs utilisés pour fournir une seule implémentation pour une abstraction était très utile; C'est cohérent avec les exemples que j'ai vus, qui sont orientés vers la configuration. Dans ce cadre, vous pouvez avoir un schéma de stratégie, mais un déploiement particulier n'aura qu'une seule implémentation configurée. – Mathias