2010-09-05 11 views
1

J'ai expérimenté avec le SimpleServiceLocator, et je l'aime un peu, mais il y a une chose que je suis vraiment frustré par - vous ne pouvez pas utiliser l'injection automatique de constructeur pour les singletons. Pour aggraver les choses, vous ne pouvez même pas utiliser l'injection automatique de constructeur pour ses dépendances . Vous devez créer l'objet singleton, toutes ses dépendances, toutes ses dépendances, etc. manuellement.SimpleServiceLocator: Pourquoi l'injection automatique de constructeur n'est-elle pas prise en charge pour les singletons?

Pourquoi SimpleServiceLocator est-il conçu de cette manière?

Les singletons ne sont-ils pas supposés être comme des instances régulières, sauf que, à la première requête d'une instance, cette instance est stockée et réutilisée au lieu qu'une nouvelle instance soit créée à chaque fois? Pourquoi SimpleServiceLocator a-t-il besoin d'une instance à fournir au cours du processus d'enregistrement plutôt que de simplement autoriser la création et le stockage de l'instance à la première demande? Je comprends que le point de SimpleServiceLocator est de ne pas avoir beaucoup de cloches et d'être très facile à utiliser pour les débutants, mais il semble que ce soit juste mal conçu, et que la méthode d'enregistrement d'un singleton soit identique à la méthode pour enregistrer une instance régulière, sauf que le nom de la méthode doit être RegisterSingle<T>() au lieu de Register<T>(). Y a-t-il une raison pour le design plus compliqué (et apparemment moins pratique) que je ne reçois pas? En attendant, existe-t-il un autre conteneur IOC (de préférence gratuit) que je peux utiliser pour enregistrer des objets dans le code de la même manière que SimpleServiceLocator, mais qui permet l'injection automatique de constructeurs pour les singletons? singleton)?

+0

Voir cet élément de travail: http://simpleservicelocator.codeplex.com/workitem/14186. – Steven

Répondre

2

La méthode RegisterSingle<T> est juste une méthode d'aide de fantaisie juste pour faciliter la vie. Ce que vous pouvez faire avec RegisterSingle<T> peut également être fait avec la méthode Register<T>. The web site gives examples of this. Vous pouvez enregistrer une seule instance en utilisant la méthode Register<T> comme suit (il utilise une fermeture):

var weapon = new Katana(); 
container.Register<IWeapon>(() => weapon); 

Lorsque vous regardez le lifestyle management examples sur le site web, vous pouvez voir l'exemple suivant pour créer un fil instance statique:

[ThreadStatic] 
private static IWeapon weapon; 

container.Register<IWeapon>(
    () => return weapon ?? (weapon = new Katana())); 

Je pense que c'est la puissance de simplify, parce qu'il n'y a presque rien que vous ne pouvez pas faire avec ce modèle. Ce que vous essayez d'accomplir est un peu plus difficile, je dois l'admettre, mais rien n'a vraiment avancé l'OMI. Voici le code dont vous avez besoin pour résoudre votre problème:

private static IWeapon weapon; 

container.Register<IWeapon>(
    () => weapon ?? (weapon = container.GetInstance<Katana>())); 

L'astuce est ici pour stocker l'instance dans une variable statique (comme avec le fil statique), mais maintenant vous ne devez pas créer l'instance vous par new mais vous déléguez la création au localisateur de service simple. Cela fonctionne parce que, comme vous le savez, SimpleServiceLocator effectuera une injection automatique de constructeur lorsqu'un type concret est demandé.

Je dois admettre que c'est une honte que nous devons faire ce truc. Ce serait bien que la bibliothèque puisse le faire pour nous. Par exemple, je peux imaginer une surcharge RegisterSingle<T> ajouté qui nous permet de faire ce qui suit:

container.RegisterSingle<IWeapon>(
    () => container.GetInstance<Katana>()); 

S'il vous plaît laissez-moi savoir ce que vous pensez d'une telle surcharge. Je suis toujours intéressé par les commentaires pour améliorer la bibliothèque. Ce serait certainement une fonctionnalité intéressante pour la prochaine version.

Mise à jour:

Depuis la version 0.14, nous pouvons faire ce qui suit:

container.RegisterSingle<IWeapon, Katana>(); 

il ne sera pas plus facile que cela.

Vive

+0

+1. Merci pour cette réponse, Steven. Je vais certainement essayer. La surcharge semble être une excellente idée. J'ai effectivement posté la même question à Codeplex comme je l'ai fait ici, mais quand j'ai eu la réponse de Jay, j'ai réalisé que je ne travaillais pas techniquement avec des singletons, alors j'ai décidé de le supprimer. Je peux mettre le remettre si tu veux, cependant. – devuxer

+0

@Dan: J'ai ajouté la surcharge 'RegisterSingle (Func )' et publié Simple Service Locator v0.8 qui contient cette surcharge. J'espère que ça aide. – Steven

+0

Ceci, c'est génial Steven, merci. Hâte de pouvoir essayer. Espérons que le lendemain ou deux. – devuxer

0

Une implémentation singleton typique a un constructeur private, de sorte que le conteneur ne peut pas le "voir", l'appeler ou détecter des dépendances. Peut-être que vous faites référence aux fonctions de gestion de durée de vie de certains conteneurs IoC, où vous pouvez configurer le conteneur pour qu'il renvoie toujours la même instance d'une classe.

Ce n'est pas ce que signifie singleton. Bien que le conteneur renvoie la même instance, rien ne vous empêche d'instancier une instance dans le code en utilisant new. D'autre part, un singleton ne peut être instancié qu'une seule fois à partir de n'importe quelle source (une fois par thread dans certaines implémentations). Il n'expose pas un constructeur public, plutôt une méthode statique telle que:

public class MySingleton 
{ 
    // note: not a thread-safe implementation 
    static MySingleton instance; 
    static DependencyThing thing; 

    private MySingleton(DependencyThing thing) 
    { 
     MySingleton.thing = thing; 
    } 

    public static MySingleton GetMySingleton(DependencyThing thing) 
    { 
     if(instance == null) instance = new MySingleton(thing); 
     return instance; 
    } 
} 

Comme vous pouvez le voir, vous ne pouvez pas appeler new MySingleton() en dehors de la classe elle-même. Pour "instancier" l'un MySingleton, vous devez appeler MySingleton.GetMySingleton(thing). Cet appel renvoie la seule instance ou crée et retourne le résultat.

SimpleServiceLocator n'a aucun moyen de savoir comment créer cet objet, ou d'où détecter ses dépendances.

Cette capacité pourrait être ajouté si l'API exposée quelque chose comme

public void Register<T>(Expression<Func<T>> staticFactoryMethod)… 

... dans ce cas, vous pouvez appeler Register(() => MySingleton.GetMySingleton());, mais cela ne fonctionne sans paramètres.Il faudrait être plus surcharge:

public void Register<T, TParam1>(Expression<Func<TParam1, T>> staticFactoryMethod)… 
public void Register<T, TParam1, TParam2>(Expression<Func<TParam1, TParam2, T>> staticFactoryMethod)… 

... de sorte que le conteneur sache quelles dépendances instancier et passer à la méthode d'usine spécifiée. Cela dit, cela n'a pas vraiment de sens d'avoir une injection de dépendance avec un singleton. Chaque appel suivant à GetMySingleton devrait ignorer les arguments ou modifier l'état du singleton, ce qui est certainement une très mauvaise idée.

+0

Merci, Jay +1. * Vous faites peut-être référence aux fonctionnalités de gestion de la durée de vie de certains conteneurs IoC, où vous pouvez configurer le conteneur pour qu'il renvoie toujours la même instance d'une classe. * Oui, c'est exactement la fonctionnalité dont j'ai besoin. Les classes que j'instancie ne sont pas techniquement singletons (il n'y a aucune garantie * qu'elles ne peuvent être instanciées qu'une fois), c'est juste que dans certains cas, j'ai besoin de certaines classes pour partager la même instance d'un objet ou il est inefficace d'instancier un objet plus d'une fois. – devuxer

+0

Cela dit, cela n'a toujours pas de sens pour moi que je ne puisse pas créer manuellement mes singletons mais utiliser l'injection automatique du constructeur pour les dépendances. Une explication pour celui-là? – devuxer

+0

@DanM Dans tous les conteneurs IoC que j'ai utilisés (plusieurs, mais pas tous), l'enregistrement de la durée de vie d'une seule instance nécessite toujours de passer dans cette instance. Supposons que le type de sujet prenne 2 paramètres de constructeur, 'ThingA' et' ThingB'. Si vous avez enregistré 'ThingA' et' ThingB', vous pouvez appeler 'container.RegisterSingle (new MySingleton (container.getInstance (), container.getInstance ())' – Jay