it-swarm.com.de

Welches Muster soll für die Protokollierung verwendet werden? Abhängigkeitsinjektion oder Service Locator?

Betrachten Sie dieses Szenario. Ich habe eine Geschäftslogik, die von Zeit zu Zeit in ein Protokoll geschrieben werden muss.

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;
    }
}

Wie würden Sie die ILogger-Schnittstelle bekommen? Ich sehe zwei Hauptmöglichkeiten;

  1. Übergeben Sie es mit Dependency Injection an den Konstruktor.
  2. Laden Sie es über einen Singleton Service Locator herunter.

Welche Methode bevorzugen Sie und warum? Oder gibt es ein noch besseres Muster?

Update: Beachten Sie, dass ich nicht alle Methodenaufrufe protokollieren müssen. Ich möchte nur einige (seltene) Ereignisse protokollieren, die innerhalb meiner Methode auftreten können oder nicht.

32
CodingInsomnia

Ich persönlich mache eine Mischung aus beidem.

Hier sind meine Konventionen:

  • Aus einem statischen Kontext - Service Location
  • Aus einem Instanzkontext - Abhängigkeitsinjektion

Ich denke, das gibt mir die richtige Balance zwischen Testbarkeit. Ich finde es etwas schwieriger, Tests für Klassen einzurichten, die Service Location verwenden, als DI zu verwenden. Aus diesem Grund ist Service Location eher die Ausnahme als die Regel. Ich bin konsequent in seiner Verwendung, so ist es nicht schwer zu merken, welche Art von Test ich schreiben muss.

Einige haben die Befürchtung geäußert, dass DI Konstruktoren überfrachtet. Ich glaube nicht, dass dies ein Problem ist, aber wenn Sie so denken, gibt es eine Reihe von Alternativen, die DI verwenden, aber Konstruktorparameter vermeiden. Hier ist eine Liste der DI-Methoden von Ninject: http://ninject.codeplex.com/wikipage?title=Injection%20Patterns

Sie werden feststellen, dass die meisten Inversion of Control-Container dieselben Funktionen wie Ninject haben. Ich habe mich für Ninject entschieden, weil sie die prägnantesten Samples haben.

Hoffentlich ist das hilfreich.

Edit: Um es klar zu machen, verwende ich Unity und Common Service Locator . Ich habe eine Singleton-Instanz meines Unity-Containers für DI und meine Implementierung von IServiceLocator ist einfach ein Wrapper um diesen Singleton-Unity-Container. Auf diese Weise muss ich keine Typenzuordnungen zweimal oder so etwas machen.

Ich finde auch nicht, dass AOP über die Rückverfolgung hinaus besonders hilfreich ist. Ich mag manuelle Protokollierung einfach wegen seiner Klarheit besser. Ich weiß, dass die meisten AOP-Protokollierungsframeworks zu beidem in der Lage sind, aber ich benötige die meiste Zeit nicht das erste (AOP's bread and butter). Dies ist natürlich nur eine persönliche Präferenz.

15
Anderson Imes

Der Logger ist eindeutig ein Dienst, von dem Ihre Geschäftslogik abhängt, und sollte daher genauso wie IDependency als Abhängigkeit behandelt werden. Injizieren Sie den Logger in Ihren Konstruktor.

Hinweis: Obwohl AOP als der Weg zur Protokollierung erwähnt wird, stimme ich nicht zu, dass es in diesem Fall die Lösung ist. AOP eignet sich hervorragend für die Ausführungsverfolgung, wird jedoch niemals eine Lösung für die Protokollierung als Teil der Geschäftslogik sein.

7
Peter Lillevold

Meine kleine Faustregel:

  • Wenn es sich um eine Klassenbibliothek handelt, verwenden Sie entweder Konstruktorinjektion oder Eigenschaftsinjektion mit einem Nullobjektmuster.

  • Wenn es sich um eine Hauptanwendung handelt, verwenden Sie den Service Locator (oder Singleton).

Ich finde das trifft ziemlich gut zu, wenn ich log4net benutze. Sie möchten nicht, dass Klassenbibliotheken auf Dinge zugreifen, die möglicherweise nicht vorhanden sind, aber in einem Anwendungsprogramm wissen Sie, dass der Logger vorhanden sein wird, und Bibliotheken wie log4net basieren stark auf dem Service-Location-Muster.

Ich neige dazu, die Protokollierung als etwas ausreichend Statisches zu betrachten, das DI nicht wirklich benötigt. Es ist äußerst unwahrscheinlich, dass ich jemals die Protokollierungsimplementierung in einer Anwendung ändern werde, zumal jedes Protokollierungsframework unglaublich flexibel und einfach zu erweitern ist. In Klassenbibliotheken ist es wichtiger, wenn Ihre Bibliothek möglicherweise von mehreren Anwendungen verwendet werden muss, die bereits verschiedene Logger verwenden.

YMMV natürlich. DI ist großartig, aber das bedeutet nicht, dass alles DI'ed werden muss.

5
Aaronaught

Wir haben alle unsere Logging/Tracing-Attribute auf PostSharp (AOP-Framework) umgestellt. Alles, was Sie tun müssen, um die Protokollierung für eine Methode zu erstellen, ist das Attribut hinzuzufügen.

Leistungen:

  • Einfache Verwendung vonAOP
  • Klare Trennung von Bedenken
  • Passiert zur Kompilierzeit -> Minimale Auswirkung auf die Leistung

Check out this .

4
ntziolis

Sie könnten einen anderen Typ ableiten, z. LoggableBusinessObject, die einen Logger in ihren Konstruktor aufnimmt. Das heißt, Sie übergeben den Logger nur für Objekte, die ihn verwenden:

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

Vielleicht ist das ein wenig offtopisch, aber warum brauchen wir überhaupt das Injizieren von Loggern, wenn wir zu Beginn der Klasse einfach tippen können:

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

Der Logger ändert sich während der Entwicklung und später während der Wartung nicht. Moderne Logger sind hochgradig anpassbar, so das Argument

was ist, wenn ich den Textlogger durch eine Datenbank ersetzen möchte?

ist vermisst.

Ich negiere nicht mit Abhängigkeitsinjektion, ich bin nur neugierig auf deinen Verstand.

3
Fka

Ich würde Singleton Service bevorzugen.

Die Abhängigkeitsinjektion würde den Konstruktor überladen.

Wenn Sie AOP verwenden können, ist dies das Beste.

1
Graviton

DI würde hier gut funktionieren. Eine andere Sache zu betrachten wäre AOP .

0
Tom Cabanski

Ich würde keinen dieser Ansätze empfehlen. Besser die aspektorientierte Programmierung. Protokollierung ist die "Hallo Welt" von AOP.

0
duffymo