web-dev-qa-db-fra.com

Quel modèle utiliser pour la journalisation? Injection de dépendance ou localisateur de service?

Considérez ce scénario. J'ai une certaine logique d'entreprise qui devra de temps en temps écrire dans un journal.

interface ILogger
{
    void Log(string stuff);
}

interface IDependency
{
    string GetInfo();
}

class MyBusinessObject
{
    private IDependency _dependency;

    public MyBusinessObject(IDependency dependency)
    {
        _dependency = dependency;
    }

    public string DoSomething(string input)
    {
        // Process input
        var info = _dependency.GetInfo();
        var intermediateResult = PerformInterestingStuff(input, info);

        if (intermediateResult== "SomethingWeNeedToLog")
        {
            // How do I get to the ILogger-interface?
        }

        var result = PerformSomethingElse(intermediateResult);

        return result;
    }
}

Comment obtiendrez-vous l'interface ILogger? Je vois deux possibilités principales;

  1. Passez-le à l'aide de l'injection de dépendance sur le constructeur.
  2. Obtenez-le via un singleton Service Locator.

Quelle méthode préféreriez-vous et pourquoi? Ou y a-t-il un modèle encore meilleur?

Mise à jour: Notez que je n'ai pas besoin de consigner TOUS les appels de méthodes. Je souhaite uniquement consigner quelques événements (rares) pouvant ou non survenir dans ma méthode.

32
CodingInsomnia

Personnellement, je fais un mélange des deux.

Voici mes conventions:

  • Dans un contexte statique - Emplacement du service
  • À partir d'un contexte d'instance - Injection de dépendance

Je pense que cela me donne le juste équilibre de testabilité. Je trouve qu'il est un peu plus difficile de configurer des tests sur des classes qui utilisent Service Location que d'utiliser DI, c'est pourquoi c'est pourquoi Service Location finit par être l'exception plutôt que la règle. Je suis cohérent dans son utilisation, cependant, donc il n’est pas difficile de se rappeler quel type de test je dois écrire.

Certains ont dit craindre que l'ID ait tendance à encombrer les constructeurs. Je ne pense pas que ce soit un problème, mais si vous en ressentez le sens, plusieurs alternatives utilisent DI, mais évitent les paramètres de constructeur. Voici une liste des méthodes DI de Ninject: http://ninject.codeplex.com/wikipage?title=Injection%20Patterns

Vous constaterez que la plupart des conteneurs Inversion of Control ont les mêmes fonctionnalités que Ninject. J'ai choisi de montrer Ninject car ils ont les échantillons les plus concis.

J'espère que cela est utile.

Edit: Pour être clair, j'utilise Unity et Common Service Locator . J'ai une instance singleton de mon conteneur Unity pour DI et mon implémentation d'IServiceLocator est simplement un wrapper autour de ce conteneur singleton Unity. De cette façon, je n'ai pas à faire de mappages de types deux fois ou quoi que ce soit du genre.

Je ne trouve pas non plus AOP particulièrement utile au-delà du traçage. J'aime mieux l'enregistrement manuel simplement pour sa clarté. Je sais que la plupart des frameworks de journalisation AOP sont capables de gérer les deux, mais je n'ai pas besoin de l'ancien (le pain quotidien de AOP) la plupart du temps. Ceci est juste une préférence personnelle, bien sûr.

15
Anderson Imes

L'enregistreur est clairement un service dont dépend votre logique métier et doit donc être traité comme une dépendance de la même manière que vous le faites avec IDependency. Injectez le logger dans votre constructeur.

Remarque: même si AOP est mentionné comme le moyen d'injecter la journalisation, je ne suis pas d'accord pour dire que c'est la solution dans ce cas. L'AOP fonctionne très bien pour le suivi de l'exécution, mais ne sera jamais une solution pour la journalisation dans le cadre de la logique métier.

7
Peter Lillevold

Ma petite règle de base:

  • Si c'est dans une bibliothèque de classes, utilisez soit l'injection de constructeur, soit l'injection de propriété avec un motif NULL-object.

  • S'il s'agit d'une application principale, utilisez le localisateur de service (ou singleton).

Je trouve que cela s’applique assez bien lors de l’utilisation de log4net. Vous ne voulez pas que les bibliothèques de classes traitent de choses qui pourraient ne pas être là, mais dans un programme d'application, vous savez que l'enregistreur va être là, et des bibliothèques comme log4net sont fortement basées sur le modèle d'emplacement de service.

J'ai tendance à penser que la journalisation est quelque chose d'assez statique qui ne nécessite pas vraiment de DI. Il est extrêmement improbable que je modifie jamais l'implémentation de la journalisation dans une application, d'autant plus que chaque infrastructure de journalisation est incroyablement flexible et facile à étendre. C'est plus important dans les bibliothèques de classes lorsque votre bibliothèque doit éventuellement être utilisée par plusieurs applications qui utilisent déjà différents enregistreurs.

YMMV, bien sûr. DI est excellent mais cela ne signifie pas que tout doit être détruit.

5
Aaronaught

Nous avons basculé tous nos attributs de journalisation/traçage sur les attributs PostSharp (structure AOP). Tout ce que vous devez faire pour créer une journalisation pour une méthode est d’ajouter l’attribut.

Avantages:

  • Utilisation facile deAOP
  • Clair séparation des préoccupations
  • Cela se produit à au moment de la compilation -> Impact minimal sur les performances

Découvrez this .

4
ntziolis

Vous pouvez dériver un autre type, par exemple LoggableBusinessObject qui prend un logger dans son constructeur. Cela signifie que vous ne transmettez à l'enregistreur que pour les objets qui l'utiliseront:

public class MyBusinessObject
{
    private IDependency _dependency;

    public MyBusinessObject(IDependency dependency)   
    {   
        _dependency = dependency;   
    }   

    public virtual string DoSomething(string input)   
    {   
        // Process input   
        var info = _dependency.GetInfo();   
        var result = PerformInterestingStuff(input, info);   
        return result;   
    }   
}

public class LoggableBusinessObject : MyBusinessObject
{
    private ILogger _logger;

    public LoggableBusinessObject(ILogger logger, IDependency dependency)
        : base(dependency)
    {
        _logger = logger;
    }

    public override string DoSomething(string input)
    {
        string result = base.DoSomething(input);
        if (result == "SomethingWeNeedToLog")
        {
             _logger.Log(result);
        }
    }
}
3
James

Peut-être que ce sera un peu hors-sujet, mais pourquoi avons-nous besoin d'injecter un enregistreur, alors que nous pouvons simplement taper au début de la classe:

Logger logger = LogManager.GetLogger("MyClassName");

L'enregistreur ne change pas pendant le développement et plus tard pendant la maintenance. Les enregistreurs modernes sont hautement personnalisables, donc argument

et si je veux remplacer l'enregistreur de texte par la base de données?

est manqué.

Je ne nie pas l'utilisation de l'injection de dépendance, je suis simplement curieux de votre esprit.

3
Fka

Je préférerais Singleton Service.

L'injection de dépendance encombrerait le constructeur.

Si vous pouvez utiliser AOP, ce serait le meilleur.

1
Graviton

DI fonctionnerait bien ici. Une autre chose à regarder serait AOP .

0
Tom Cabanski

Je recommande aucune de ces approches. Il vaut mieux utiliser la programmation orientée aspect. L’exploitation forestière est le «monde du salut» de l’AOP.

0
duffymo