Ich hatte die Angewohnheit, Logger an Konstruktor weiterzugeben, wie:
public class OrderService : IOrderService {
public OrderService(ILogger logger) {
}
}
Aber das ist ziemlich ärgerlich, deshalb habe ich es für einige Zeit als Eigenschaft benutzt:
private ILogger logger = NullLogger.Instance;
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}
Das wird auch nervig - es ist nicht trocken, ich muss das in jeder Klasse wiederholen. Ich könnte die Basisklasse verwenden, aber andererseits - ich verwende die Form-Klasse, würde also FormBase usw. benötigen. Ich denke also, was wäre der Nachteil, wenn Singleton mit ILogger verfügbar gemacht würde, damit jeder weiß, wo er Logger bekommt:
Infrastructure.Logger.Info("blabla");
UPDATE: Wie Merlyn richtig bemerkt hat, sollte ich erwähnen, dass ich im ersten und zweiten Beispiel DI verwende.
Antworten:
Das stimmt. Aber es gibt nur so viel, was Sie für ein Querschnittsthema tun können, das jeden Typ durchdringt, den Sie haben. Sie müssen verwenden Sie den Logger überall, so dass Sie die Eigenschaft auf diesen Typen haben müssen.
Mal sehen, was wir dagegen tun können.
Singleton
Singletons sind schrecklich
<flame-suit-on>
.Ich empfehle, bei der Eigenschaftsinjektion zu bleiben, wie Sie es bei Ihrem zweiten Beispiel getan haben. Dies ist das beste Factoring, das Sie durchführen können, ohne auf Magie zurückzugreifen. Es ist besser, eine explizite Abhängigkeit zu haben, als sie über einen Singleton auszublenden.
Aber wenn Singletons Ihnen viel Zeit sparen, einschließlich aller Umgestaltungen, die Sie jemals durchführen müssen (Kristallkugelzeit!), Können Sie wahrscheinlich mit ihnen leben. Wenn es jemals eine Verwendung für einen Singleton gab, könnte dies der Fall sein. Denken Sie daran , dass die Kosten, wenn Sie jemals Ihre Meinung ändern möchten, so hoch wie möglich sind.
Wenn Sie dies tun, überprüfen Sie die Antworten anderer Personen anhand des
Registry
Musters (siehe Beschreibung) und derjenigen, die eine (zurücksetzbare) Singleton- Factory anstelle einer Singleton-Logger-Instanz registrieren .Es gibt andere Alternativen, die ohne Kompromisse genauso gut funktionieren könnten. Sie sollten sie daher zuerst überprüfen.
Visual Studio-Codefragmente
Sie können Visual Studio-Codefragmente verwenden , um die Eingabe dieses sich wiederholenden Codes zu beschleunigen. Sie können so etwas
logger
tabeingeben und der Code wird auf magische Weise für Sie angezeigt.Mit AOP abtrocknen
Sie können ein wenig von diesem Eigenschaftsinjektionscode entfernen , indem Sie ein AOP-Framework (Aspect Oriented Programming) wie PostSharp verwenden, um einen Teil davon automatisch zu generieren.
Es könnte ungefähr so aussehen, wenn Sie fertig sind:
[InjectedLogger] public ILogger Logger { get; set; }
Sie können auch den Beispielcode für die Methodenverfolgung verwenden, um den Eingangs- und Ausgangscode für Methoden automatisch zu verfolgen, sodass möglicherweise nicht alle Logger-Eigenschaften zusammengefügt werden müssen. Sie können das Attribut auf Klassenebene oder im gesamten Namespace anwenden:
[Trace] public class MyClass { // ... } // or #if DEBUG [assembly: Trace( AttributeTargetTypes = "MyNamespace.*", AttributeTargetTypeAttributes = MulticastAttributes.Public, AttributeTargetMemberAttributes = MulticastAttributes.Public )] #endif
quelle
</flame-suit-on>
Ich weiß nicht, wie Sie 5 Jahre in einem Flammenanzug gelebt haben, aber ich hoffe, das hilft.Ich habe eine Logger-Instanz in meinen Abhängigkeitsinjektionscontainer eingefügt, die den Logger dann in die Klassen injiziert, die eine benötigen.
quelle
Gute Frage. Ich glaube, in den meisten Projekten ist Logger ein Singleton.
Einige Ideen kommen mir gerade in den Sinn:
Object
Typ so würde jede Klasse in der Lage sein mag Loggers Methoden aufrufenLogInfo()
,LogDebug()
,LogError()
quelle
public static void LogInfo(this Object instance, string message)
so dass jede Klasse sie aufgreifen würde. In Bezug aufServiceLocator
- dies ermöglicht es Ihnen, Logger als reguläre Klasseninstanz und nicht als Singleton zu verwenden, sodass Sie viel Flexibilität geben würdenEin Singleton ist eine gute Idee. Eine noch bessere Idee ist die Verwendung des Registrierungsmusters , mit dem die Instanziierung etwas besser gesteuert werden kann. Meiner Meinung nach liegt das Singleton-Muster zu nahe an globalen Variablen. Bei einer Registrierung, die die Objekterstellung oder -wiederverwendung verwaltet, ist Platz für zukünftige Änderungen der Instanziierungsregeln.
Die Registrierung selbst kann eine statische Klasse sein, um eine einfache Syntax für den Zugriff auf das Protokoll bereitzustellen:
Registry.Logger.Info("blabla");
quelle
Ein einfacher Singleton ist keine gute Idee. Es macht es schwierig, den Logger auszutauschen. Ich neige dazu, Filter für meine Logger zu verwenden (einige "verrauschte" Klassen protokollieren möglicherweise nur Warnungen / Fehler).
Ich verwende ein Singleton-Muster in Kombination mit dem Proxy-Muster für die Logger-Factory:
public class LogFactory { private static LogFactory _instance; public static void Assign(LogFactory instance) { _instance = instance; } public static LogFactory Instance { get { _instance ?? (_instance = new LogFactory()); } } public virtual ILogger GetLogger<T>() { return new SystemDebugLogger(); } }
Dies ermöglicht es mir, einen
FilteringLogFactory
oder nur einen zu erstellen,SimpleFileLogFactory
ohne Code zu ändern (und daher dem Open / Closed-Prinzip zu entsprechen).Beispielerweiterung
public class FilteredLogFactory : LogFactory { public override ILogger GetLogger<T>() { if (typeof(ITextParser).IsAssignableFrom(typeof(T))) return new FilteredLogger(typeof(T)); return new FileLogger(@"C:\Logs\MyApp.log"); } }
Und um die neue Fabrik zu nutzen
// and to use the new log factory (somewhere early in the application): LogFactory.Assign(new FilteredLogFactory());
In Ihrer Klasse sollte das protokollieren:
public class MyUserService : IUserService { ILogger _logger = LogFactory.Instance.GetLogger<MyUserService>(); public void SomeMethod() { _logger.Debug("Welcome world!"); } }
quelle
Instance
im Anwendungsstamm festlegen und ich weiß es nicht warum Sie es zurücksetzen würden)In .NET gibt es ein Buch Dependency Injection. Je nachdem, was Sie benötigen, sollten Sie das Abfangen verwenden.
In diesem Buch gibt es ein Diagramm, das bei der Entscheidung hilft, ob Konstruktorinjektion, Eigenschaftsinjektion, Methodeninjektion, Umgebungskontext und Abfangen verwendet werden sollen.
So begründet man die Verwendung dieses Diagramms:
Verwenden Sie Interception
quelle
Eine andere Lösung, die ich persönlich am einfachsten finde, ist die Verwendung einer statischen Logger-Klasse. Sie können es von jeder Klassenmethode aus aufrufen, ohne die Klasse ändern zu müssen, z. B. Eigenschaftsinjektion hinzufügen usw. Es ist ziemlich einfach und benutzerfreundlich.
Logger::initialize ("filename.log", Logger::LEVEL_ERROR); // only need to be called once in your application Logger::log ("my error message", Logger::LEVEL_ERROR); // to be used in every method where needed
quelle
Wenn Sie sich eine gute Lösung für die Protokollierung ansehen möchten, empfehlen wir Ihnen, sich die Google App Engine mit Python anzusehen, bei der die Protokollierung so einfach wie möglich ist.
import logging
Dann können Sie sie einfachlogging.debug("my message")
oderlogging.info("my message")
so einfach halten, wie sie sollte.Java hatte keine gute Lösung für die Protokollierung, dh log4j sollte vermieden werden, da es Sie praktisch dazu zwingt, Singletons zu verwenden, was, wie hier beantwortet, "schrecklich" ist, und ich habe schreckliche Erfahrungen damit gemacht, die Protokollausgabe nur einmal mit derselben Protokollierungsanweisung zu versehen wenn ich vermute, dass der Grund für die doppelte Protokollierung darin bestand, dass ich einen Singleton des Protokollierungsobjekts in zwei Klassenladeprogrammen in derselben virtuellen Maschine (!) habe.
Ich bitte um Verzeihung, dass Sie nicht so spezifisch für C # sind, aber nach dem, was ich gesehen habe, sehen die Lösungen mit C # ähnlich aus wie Java, wo wir log4j hatten, und wir sollten es auch zu einem Singleton machen.
Aus diesem Grund hat mir die Lösung mit GAE / Python sehr gut gefallen. Sie ist so einfach wie möglich und Sie müssen sich keine Gedanken über Klassenladeprogramme, doppelte Protokollierungsanweisungen oder Designmuster machen.
Ich hoffe, dass einige dieser Informationen für Sie relevant sein können, und ich hoffe, dass Sie einen Blick auf die von mir empfohlene Protokollierungslösung werfen möchten. Stattdessen schikaniere ich, wie viel Problem Singleton aufgrund der Unmöglichkeit, wann einen echten Singleton zu haben, vermutet wird es muss in mehreren Klassenladern instanziiert werden.
quelle
using Logging; /* ... */ Logging.Info("my message");
logging.info("my message")
in einem Programm, das komplizierter ist als die Hallo Welt. Normalerweise führen Sie eine ganze Reihe von Boilerplates durch, um den Logger zu initialisieren. Sie richten den Formatierer und die Ebene ein und richten sowohl Datei- als auch Konsolenhandler ein. Es ist nie jemalslogging.info("my message")
!