Myśląc o zwinnym tworzeniu oprogramowania i wszystkich zasadach (SRP, OCP, ...) zadaję sobie pytanie, jak traktować rejestrowanie.
Czy logowanie obok implementacji stanowi naruszenie zasad SRP?
Powiedziałbym, yes
ponieważ wdrożenie powinno być również w stanie działać bez logowania. Jak więc lepiej wdrożyć rejestrowanie? Sprawdziłem niektóre wzory i doszedłem do wniosku, że najlepszym sposobem, aby nie naruszać zasad w sposób zdefiniowany przez użytkownika, ale użyć dowolnego wzoru, o którym wiadomo, że narusza zasadę, jest użycie wzoru dekoratora.
Załóżmy, że mamy kilka komponentów całkowicie bez naruszenia SRP, a następnie chcemy dodać rejestrowanie.
- składnik A
- składnik B wykorzystuje A
Chcemy logowania dla A, więc tworzymy kolejny komponent D ozdobiony A, oba implementujące interfejs I.
- interfejs I
- komponent L (komponent rejestrujący systemu)
- składnik A implementuje I
- składnik D implementuje I, ozdabia / używa A, używa L do logowania
- składnik B wykorzystuje I
Zalety: - Mogę używać A bez logowania - testowanie A oznacza, że nie potrzebuję żadnych próbnych rejestrów - testy są prostsze
Wada: - więcej komponentów i więcej testów
Wiem, że to wydaje się być kolejnym otwartym pytaniem do dyskusji, ale tak naprawdę chcę wiedzieć, czy ktoś stosuje lepsze strategie rejestrowania niż naruszenie dekoratora lub SRP. Co z statycznym logowaniem singleton, które są domyślnie NullLogger, a jeśli potrzebne jest rejestrowanie syslog, należy zmienić obiekt implementacji w czasie wykonywania?
Odpowiedzi:
Tak, jest to naruszenie SRP, ponieważ rejestracja jest zagadnieniem przekrojowym.
Prawidłowym sposobem jest przekazanie logowania do klasy rejestratora (przechwytywanie), której jedynym celem jest rejestrowanie - zgodnie z SRP.
Zobacz ten link jako dobry przykład: https://msdn.microsoft.com/en-us/library/dn178467%28v=pandp.30%29.aspx
Oto krótki przykład :
Korzyści obejmują
źródło
TenantStoreLogger
zmienisz się za każdym razem, gdy sięTenantStore
zmieni. Nie rozdzielasz problemów bardziej niż w pierwotnym rozwiązaniu.Powiedziałbym, że zbyt poważnie podchodzisz do SRP. Jeśli Twój kod jest wystarczająco uporządkowany, aby logowanie było jedynym „naruszeniem” SRP, radzisz sobie lepiej niż 99% wszystkich innych programistów i powinieneś poklepać się po plecach.
Celem SRP jest unikanie przerażającego kodu spaghetti, w którym kod, który robi różne rzeczy, jest pomieszany. Mieszanie rejestrowania z kodem funkcjonalnym nie wywołuje dla mnie żadnych dzwonków alarmowych.
źródło
Do you mock the logger?
:, to jest PRECYZYJNIE to, co robisz. Powinieneś miećILogger
interfejs, który określa CO robi rejestrator. Testowany kod jest wstrzykiwany z określonymILogger
przez Ciebie. Do testowania maszclass TestLogger : ILogger
. Wspaniałą rzeczą w tym jest to, żeTestLogger
może ujawniać rzeczy takie jak ostatni zarejestrowany ciąg lub błąd. Testy mogą sprawdzić, czy testowany kod loguje się poprawnie. Na przykład testem może byćUserSignInTimeGetsLogged()
test sprawdzaniaTestLogger
zalogowanego.Nie, to nie jest naruszenie SRP.
Wiadomości wysyłane do dziennika powinny ulec zmianie z tych samych powodów, co otaczający kod.
Czym jest naruszenie SRP, polega na użyciu określonej biblioteki do logowania bezpośrednio w kodzie. Jeśli zdecydujesz się zmienić sposób rejestrowania, SRP stwierdza, że nie powinno to wpływać na kod biznesowy.
Logger
Kod abstrakcyjny powinien być dostępny dla pewnego rodzaju streszczenia , a jedyną rzeczą, którą powinna powiedzieć implementacja, jest „Wyślij tę wiadomość do dziennika”, bez obaw o sposób jej wykonania. Decydowanie o dokładnym sposobie rejestrowania (nawet znaczników czasu) nie jest obowiązkiem twojej implementacji.Twoja implementacja nie powinna również wiedzieć, czy program rejestrujący, do którego wysyła wiadomości, jest
NullLogger
.To mówi.
Nie odrzuciłbym zbyt szybko wylogowywania się jako problemu przekrojowego . Emitowanie dzienników w celu śledzenia określonych zdarzeń występujących w kodzie implementacji należy do kodu implementacji.
OTOH jest zagadnieniem przekrojowym. Śledzenie wykonania : rejestrowanie wchodzi i wychodzi w każdej metodzie. AOP jest najlepiej przygotowany do tego.
źródło
Login
interfejs ozdobiony tym samym rejestratorem.Ponieważ rejestrowanie jest często uważane za problem przekrojowy, sugeruję użycie AOP do oddzielenia rejestrowania od implementacji.
W zależności od języka użyjesz do tego celu przechwytywacza lub jakiegoś środowiska AOP (np. AspectJ w Javie).
Pytanie brzmi, czy to naprawdę jest kłopotliwe. Pamiętaj, że ta separacja zwiększy złożoność projektu, zapewniając jednocześnie bardzo niewielkie korzyści.
źródło
Brzmi nieźle. Opisujesz dość standardowy dekorator drewna. Ty masz:
Na tym spoczywa jedna odpowiedzialność: rejestrowanie przekazywanych mu informacji.
Ma to jeden obowiązek: zapewnienie implementacji interfejsu I (przy założeniu, że jestem odpowiednio zgodny z SRP).
To jest kluczowa część:
Mówiąc w ten sposób, brzmi to skomplikowanie, ale spójrz na to w ten sposób: Komponent D robi jedną rzecz: łączy A i L.
Tylko odpowiedzialność składnik D ma to, aby upewnić się, że L jest powiadamiany, gdy stosuje się. Implementacje A i L znajdują się gdzie indziej. Jest to całkowicie zgodne z SRP, a także jest dobrym przykładem OCP i dość powszechnym zastosowaniem dekoratorów.
Ważne zastrzeżenie: gdy D używa komponentu rejestrującego L, powinno to robić w sposób umożliwiający zmianę sposobu rejestrowania. Najprostszym sposobem na to jest posiadanie interfejsu IL, który jest implementowany przez L. Następnie:
W ten sposób nic nie zależy bezpośrednio od niczego innego, co ułatwia ich wymianę. Ułatwia to dostosowanie się do zmian i kpiny z części systemu, dzięki czemu można przeprowadzić test jednostkowy.
źródło
D implements I
. Dziękuję za Twoją odpowiedź.Oczywiście jest to naruszenie SRP, ponieważ masz problem przekrojowy. Możesz jednak utworzyć klasę odpowiedzialną za tworzenie rejestrowania przy wykonywaniu dowolnej akcji.
przykład:
źródło