Miałem zwyczaj przekazywania loggera do konstruktora, na przykład:
public class OrderService : IOrderService {
public OrderService(ILogger logger) {
}
}
Ale to dość denerwujące, więc od jakiegoś czasu używam tej właściwości:
private ILogger logger = NullLogger.Instance;
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}
To też robi się irytujące - nie wysycha, muszę to powtarzać na każdych zajęciach. Mógłbym użyć klasy bazowej, ale z drugiej strony - używam klasy Form, więc potrzebowałbym FormBase itp. Myślę więc, jaka byłaby wada posiadania singletona z ujawnionym ILoggerem, więc każdy wiedziałby, skąd wziąć rejestrator:
Infrastructure.Logger.Info("blabla");
AKTUALIZACJA: Jak słusznie zauważył Merlyn, powinienem wspomnieć, że w pierwszym i drugim przykładzie używam DI.
Odpowiedzi:
To prawda. Ale tylko tyle możesz zrobić dla przekrojowego problemu, który przenika każdy twój typ. Musisz używać loggera wszędzie, więc musisz mieć właściwość na tych typach.
Zobaczmy więc, co możemy z tym zrobić.
Singel
Singletony są okropne
<flame-suit-on>
.Zalecam trzymać się wstrzykiwania właściwości, tak jak zrobiłeś to na drugim przykładzie. To najlepszy faktoring, jaki możesz zrobić bez uciekania się do magii. Lepiej jest mieć wyraźną zależność niż ukrywać ją za pomocą singletona.
Ale jeśli singletony zaoszczędzą ci dużo czasu, w tym wszystkie refaktoryzacje, które będziesz musiał kiedykolwiek wykonać (czas kryształowej kuli!), Przypuszczam, że możesz z nimi żyć. Jeśli Singleton kiedykolwiek miałby zastosowanie, to może być to. Pamiętaj, że koszt, jeśli kiedykolwiek zechcesz zmienić zdanie, będzie tak wysoki, jak to tylko możliwe.
Jeśli to zrobisz, sprawdź odpowiedzi cudzych korzystania z
Registry
wzoru (patrz opis), a ci rejestracji (Reset możliwy) singleton fabryki zamiast instancji Singleton rejestratora.Istnieją inne alternatywy, które mogą działać równie dobrze bez większego kompromisu, więc powinieneś je najpierw sprawdzić.
Fragmenty kodu programu Visual Studio
Możesz użyć fragmentów kodu programu Visual Studio, aby przyspieszyć wprowadzanie tego powtarzalnego kodu. Będziesz mógł wpisać coś takiego
logger
tab, a kod magicznie pojawi się dla Ciebie.Używanie AOP do SUSZENIA
Możesz wyeliminować trochę tego kodu iniekcji właściwości, używając struktury programowania zorientowanego na aspekty (AOP), takiej jak PostSharp, aby automatycznie wygenerować część z nich.
Kiedy skończysz, może to wyglądać mniej więcej tak:
[InjectedLogger] public ILogger Logger { get; set; }
Możesz również użyć ich przykładowego kodu śledzenia metod, aby automatycznie śledzić kod wejścia i wyjścia metody, co może wyeliminować potrzebę dodawania wszystkich właściwości programu rejestrującego. Możesz zastosować atrybut na poziomie klasy lub całej przestrzeni nazw:
[Trace] public class MyClass { // ... } // or #if DEBUG [assembly: Trace( AttributeTargetTypes = "MyNamespace.*", AttributeTargetTypeAttributes = MulticastAttributes.Public, AttributeTargetMemberAttributes = MulticastAttributes.Public )] #endif
źródło
</flame-suit-on>
Nie wiem, jak żyłeś w kombinezonie płomieni przez 5 lat, ale mam nadzieję, że to pomoże.Umieszczam wystąpienie rejestratora w moim kontenerze iniekcji zależności, który następnie wstrzykuje rejestrator do klas, które go potrzebują.
źródło
Dobre pytanie. Uważam, że w większości projektów rejestrator jest singletonem.
Przychodzi mi do głowy kilka pomysłów:
Object
typu więc każda klasa będzie w stanie wywołać metody rejestratora podobaLogInfo()
,LogDebug()
,LogError()
źródło
public static void LogInfo(this Object instance, string message)
sposób, aby każda klasa je odebrała, OdnośnieServiceLocator
- pozwala to na posiadanie loggera jako zwykłej instancji klasy, a nie singletona, więc dałbyś dużą elastycznośćSingleton to dobry pomysł. Jeszcze lepszym pomysłem jest użycie wzorca Registry , który daje nieco większą kontrolę nad instancjami. Moim zdaniem wzorzec singletona jest zbyt blisko zmiennych globalnych. Dzięki rejestrowi obsługującemu tworzenie lub ponowne wykorzystywanie obiektów jest miejsce na przyszłe zmiany reguł tworzenia instancji.
Sam rejestr może być klasą statyczną, która zapewnia prostą składnię dostępu do dziennika:
Registry.Logger.Info("blabla");
źródło
Zwykły singleton nie jest dobrym pomysłem. Utrudnia to wymianę rejestratora. Zwykle używam filtrów dla moich rejestratorów (niektóre „hałaśliwe” klasy mogą rejestrować tylko ostrzeżenia / błędy).
Używam wzorca singleton w połączeniu ze wzorcem proxy dla fabryki loggera:
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(); } }
To pozwala mi na tworzenie
FilteringLogFactory
lub po prostuSimpleFileLogFactory
bez zmiany kodu (a zatem zgodnie z zasadą Open / Closed).Przykładowe rozszerzenie
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"); } }
I do korzystania z nowej fabryki
// and to use the new log factory (somewhere early in the application): LogFactory.Assign(new FilteredLogFactory());
W Twojej klasie, która powinna rejestrować:
public class MyUserService : IUserService { ILogger _logger = LogFactory.Instance.GetLogger<MyUserService>(); public void SomeMethod() { _logger.Debug("Welcome world!"); } }
źródło
Instance
w katalogu głównym aplikacji i nie wiem dlaczego to zresetowałeś)Istnieje książka Dependency Injection w .NET. W oparciu o to, czego potrzebujesz, powinieneś użyć przechwytywania.
W tej książce znajduje się diagram pomagający zdecydować, czy użyć iniekcji konstruktora, iniekcji właściwości, iniekcji metody, kontekstu otoczenia, przechwytywania.
Oto jeden powód używania tego diagramu:
Użyj przechwytywania
źródło
Innym rozwiązaniem, które osobiście uważam za najłatwiejsze, jest użycie statycznej klasy Logger. Możesz go wywołać z dowolnej metody klasy bez konieczności zmiany klasy, np. Dodawania iniekcji właściwości itp. Jest dość prosty i łatwy w użyciu.
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
źródło
Jeśli chcesz spojrzeć na dobre rozwiązanie do logowania, proponuję przyjrzeć się silnikowi aplikacji Google z Pythonem, gdzie logowanie jest tak proste, jak
import logging
i wtedy możesz po prostulogging.debug("my message")
lublogging.info("my message")
który naprawdę sprawia, że jest tak proste, jak powinno.Java nie miała dobrego rozwiązania do rejestrowania, tj. Należy unikać log4j, ponieważ praktycznie zmusza cię to do używania singletonów, które, jak tu podano, są "okropne" i miałem okropne doświadczenie z próbą uczynienia wyjścia logowania tą samą instrukcją logowania tylko raz kiedy podejrzewam, że powodem podwójnego logowania było to, że mam jeden obiekt logowania w dwóch klasach ładujących w tej samej maszynie wirtualnej (!)
Przepraszam, że nie jest tak specyficzny dla C #, ale z tego, co widziałem, rozwiązania z C # wyglądają podobnie do Javy, gdzie mieliśmy log4j i powinniśmy również uczynić go singletonem.
Dlatego bardzo podobało mi się rozwiązanie z GAE / pythonem , jest tak proste, jak to tylko możliwe i nie musisz martwić się o ładowanie klas, otrzymywanie instrukcji podwójnego logowania lub w ogóle żadnego wzoru projektu.
Mam nadzieję, że niektóre z tych informacji mogą być dla Ciebie istotne i mam nadzieję, że zechcesz rzucić okiem na moje rozwiązanie do logowania, które polecam, zamiast tego zastraszam, jak duży problem podejrzewa się Singletona z powodu niemożności posiadania prawdziwego singletona, gdy musi być instancjowany w kilku programach ładujących.
źródło
using Logging; /* ... */ Logging.Info("my message");
logging.info("my message")
w programie bardziej skomplikowanym niż Hello world. Zwykle wykonujesz wiele standardowych czynności inicjujących rejestrator - ustawiając program formatujący, poziom, konfigurując zarówno obsługę plików, jak i konsoli. To nigdy przenigdylogging.info("my message")
!