145 votes

Est-il un modèle pour l'initialisation des objets créés par l'intermédiaire d'un conteneur d'injection de dépendances

Je suis en train d'essayer d'obtenir l'Unité pour gérer la création de mes objets et je veux avoir certains paramètres d'initialisation qui ne sont connus qu'au moment de l'exécution:

Pour le moment la seule façon que je pouvais penser de la façon de le faire est d'avoir une méthode d'Initialisation de l'interface.

interface IMyIntf {
  void Initialize(string runTimeParam);
  string RunTimeParam { get; }
}

Puis de l'utiliser (dans l'Unité), je ferais ceci:

var IMyIntf = unityContainer.Resolve<IMyIntf>();
IMyIntf.Initialize("somevalue");

Dans ce scénario, runTimeParam param est déterminé au moment de l'exécution en fonction de l'entrée utilisateur. Le cas trivial ici renvoie simplement la valeur de runTimeParam mais en réalité, le paramètre sera quelque chose comme nom de fichier et la méthode initialize de faire quelque chose avec le fichier.

Cela crée un certain nombre de questions, à savoir que l' Initialize la méthode est disponible sur l'interface et peut être appelée plusieurs fois. Définition d'un indicateur dans la mise en œuvre et de les jeter exception sur les appels répétés à l' Initialize semble de façon maladroite.

Au point où j'ai régler mon interface, je ne veux pas savoir quelque chose au sujet de la mise en œuvre de l' IMyIntf. Ce que je veux, cependant, est le fait de savoir que l'interface a besoin d'un temps d'initialisation des paramètres. Est-il un moyen en quelque sorte de les annoter(attributs?) l'interface avec ces informations, celles-cadre lorsque l'objet est créé?

Edit: Décrit l'interface un peu plus.

272voto

Mark Seemann Points 102767

Tout endroit où vous avez besoin d'une valeur d'exécution pour la construction d'un particulier de la dépendance, Résumé de l'Usine est la solution.

Avoir des méthodes Initialize sur les interfaces odeurs d'une Abstraction qui Fuit.

Dans votre cas, je dirais que vous devriez modèle de la IMyIntf interface sur la façon dont vous devez utiliser - pas de la façon dont vous l'intention de créer des implémentations de celle-ci. C'est un détail d'implémentation.

Ainsi, l'interface devrait tout simplement être:

public interface IMyIntf
{
    string RunTimeParam { get; }
}

Définissez maintenant le Résumé de l'Usine:

public interface IMyIntfFactory
{
    IMyIntf Create(string runTimeParam);
}

Vous pouvez maintenant créer une mise en œuvre concrète de IMyIntfFactory qui crée des exemples concrets de IMyIntf comme celui-ci:

public class MyIntf : IMyIntf
{
    private readonly string runTimeParam;

    public MyIntf(string runTimeParam)
    {
        if(runTimeParam == null)
        {
            throw new ArgumentNullException("runTimeParam");
        }

        this.runTimeParam = runTimeParam;
    }

    public string RunTimeParam
    {
        get { return this.runTimeParam; }
    }
}

Remarquez comment cela nous permet de protéger la classe des " invariants par l'utilisation de l' readonly mot-clé. Pas de malodorante Initialiser méthodes sont nécessaires.

Un IMyIntfFactory mise en œuvre peut être aussi simple que cela:

public class MyIntfFactory : IMyIntfFactory
{
    public IMyIntf Create(string runTimeParam)
    {
        return new MyIntf(runTimeParam);
    }
}

Dans toutes vos consommateurs si vous avez besoin d'un IMyIntf exemple, il vous suffit de prendre une dépendance sur IMyIntfFactory en demandant à Constructeur d'Injection.

Tout Conteneur d'injection de dépendances qui vaut son sel sera capable d'auto-fils d'un IMyIntfFactory exemple pour vous si vous vous inscrivez correctement.

15voto

Phil Sandler Points 12937

Habituellement, lorsque vous rencontrez cette situation, vous avez besoin de revoir votre conception et de déterminer si vous mélangez votre stateful/objets de données avec pure des services. Dans la plupart (pas tous) des cas, vous aurez envie de garder ces deux types d'objets distincts.

Si vous avez besoin d'un contexte spécifique du paramètre passé dans le constructeur, une option est de créer une usine qui résout vos dépendances de service via le constructeur, et prend votre paramètre d'exécution en tant que paramètre de la méthode Create () (ou Generate(), Construire (ou ce que vous nommez vos méthodes de fabrique).

Avoir des incubateurs ou une méthode Initialize() sont généralement pensé pour être une mauvaise conception, que vous avez besoin de "se souvenir" de les appeler et être sûr de ne pas ouvrir trop de votre mise en œuvre de l'état (c'est à dire qu'est-ce que pour empêcher quelqu'un de re-appel d'initialiser ou le setter?).

5voto

jigamiller Points 81

J'ai aussi trouvé cette situation à quelques reprises dans des environnements où je suis, la création dynamique d'ViewModel des objets en se basant sur les objets de Modèle (décrit très bien par cet autre Stackoverflow post).

J'ai aimé la façon dont l' Ninject extension qui permet de créer dynamiquement des usines basée sur des interfaces:

Bind<IMyFactory>().ToFactory();

Je ne pouvais pas trouver tout de fonctionnalités similaires, directement dans l'Unité; j'ai donc écrit ma propre extension de la IUnityContainer qui vous permet de vous inscrire usines qui permettra de créer de nouveaux objets en se basant sur des données à partir d'objets existants essentiellement cartographie à partir d'une hiérarchie de type vers un autre type de hiérarchie: UnityMappingFactory@GitHub

Avec un objectif de simplicité et de lisibilité, j'ai fini avec une extension qui vous permet de spécifier directement les mappages sans déclaration individuelle d'usine, des interfaces ou des classes (un vrai gain de temps). Il suffit d'ajouter les mappages de droit dans lequel vous inscrivez le cours normal de l'amorçage du processus de...

//make sure to register the output...
container.RegisterType<IImageWidgetViewModel, ImageWidgetViewModel>();
container.RegisterType<ITextWidgetViewModel, TextWidgetViewModel>();

//define the mapping between different class hierarchies...
container.RegisterFactory<IWidget, IWidgetViewModel>()
.AddMap<IImageWidget, IImageWidgetViewModel>()
.AddMap<ITextWidget, ITextWidgetViewModel>();

Alors que vous venez de déclarer la cartographie de l'usine de l'interface dans le constructeur pour l'IC et de l'utilisation de ses Create() la méthode...

public ImageWidgetViewModel(IImageWidget widget, IAnotherDependency d) { }

public TextWidgetViewModel(ITextWidget widget) { }

public ContainerViewModel(object data, IFactory<IWidget, IWidgetViewModel> factory)
{
    IList<IWidgetViewModel> children = new List<IWidgetViewModel>();
    foreach (IWidget w in data.Widgets)
        children.Add(factory.Create(w));
}

Comme un bonus supplémentaire, toutes les dépendances supplémentaires dans le constructeur de la mappé classes obtiendrez également résolu lors de la création de l'objet.

Évidemment, cela ne va pas résoudre tous les problèmes, mais il a servi de moi assez bien pour l'instant alors j'ai pensé que je devais le partager. Il n'y a plus de documentation sur le site du projet sur GitHub.

1voto

anthony Points 15067

Je ne peux pas répondre avec spécifique de l'Unité de terminologie, mais il semble que vous êtes seulement à apprendre à propos de l'injection de dépendance. Si si, je vous invite à lire la brève, claire, et des informations emballé guide de l'utilisateur pour Ninject.

Cela vous guidera à travers les différentes options que vous avez lors de l'utilisation de DI, et de la manière de prendre en compte les problèmes spécifiques que vous devrez faire face le long du chemin. Dans votre cas, vous souhaiterez probablement utiliser le conteneur d'injection de dépendances pour instancier des objets, et l'objet obtenir une référence à chacun de ses dépendances par le constructeur.

La procédure pas à pas décrit également comment annoter les méthodes, les propriétés et même les paramètres à l'aide des attributs de les distinguer lors de l'exécution.

Même si vous n'utilisez pas Ninject, la procédure pas à pas vous donner les concepts et la terminologie de la fonctionnalité qui convient à votre objectif, et vous devriez être en mesure de cartographier les connaissances à l'Unité ou d'autres DI cadres (ou convaincre yout pour donner Ninject un essai).

1voto

Igor Zevaka Points 32586

Je pense que je l'ai résolu, et il se sent plutôt sain, de sorte qu'il doit être à moitié raison :))

J'ai divisé IMyIntf en un "getter" et "setter" interfaces. Donc:

interface IMyIntf {
  string RunTimeParam { get; }
}


interface IMyIntfSetter {
  void Initialize(string runTimeParam);
  IMyIntf MyIntf {get; }
}

Ensuite, la mise en œuvre:

class MyIntfImpl : IMyIntf, IMyIntfSetter {
  string _runTimeParam;

  void Initialize(string runTimeParam) {
    _runTimeParam = runTimeParam;
  }

  string RunTimeParam { get; }

  IMyIntf MyIntf {get {return this;} }
}

//Unity configuration:
//Only the setter is mapped to the implementation.
container.RegisterType<IMyIntfSetter, MyIntfImpl>();
//To retrieve an instance of IMyIntf:
//1. create the setter
IMyIntfSetter setter = container.Resolve<IMyIntfSetter>();
//2. Init it
setter.Initialize("someparam");
//3. Use the IMyIntf accessor
IMyIntf intf = setter.MyIntf;

IMyIntfSetter.Initialize() peut encore être appelé plusieurs fois, mais à l'aide de bits de Service Localisateur de paradigme que nous pouvons l'envelopper tout à fait bien, de sorte que IMyIntfSetter est presque une interface interne qui est distincte de l' IMyIntf.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X