Раніше я використовував фасади реєстрації, такі як Common.Logging (навіть для того, щоб приховати власну бібліотеку CuttingEdge.Logging ), але сьогодні я використовую шаблон введення залежності, і це дозволяє мені приховувати реєстратори за власною (простою) абстракцією, яка дотримується обох залежностей Принцип інверсії та Принцип сегрегації інтерфейсу(ISP), оскільки він має одного члена і тому, що інтерфейс визначається моєю програмою; не зовнішня бібліотека. Тим менше, мінімізуючи знання основних частин вашої програми про існування зовнішніх бібліотек; навіть якщо ви не маєте наміру ніколи замінювати свою бібліотеку реєстрації. Важка залежність від зовнішньої бібліотеки ускладнює тестування коду та ускладнює програму за допомогою API, який ніколи не був розроблений спеціально для вашої програми.
Ось так часто виглядає абстракція в моїх додатках:
public interface ILogger
{
void Log(LogEntry entry);
}
public enum LoggingEventType { Debug, Information, Warning, Error, Fatal };
// Immutable DTO that contains the log information.
public class LogEntry
{
public readonly LoggingEventType Severity;
public readonly string Message;
public readonly Exception Exception;
public LogEntry(LoggingEventType severity, string message, Exception exception = null)
{
if (message == null) throw new ArgumentNullException("message");
if (message == string.Empty) throw new ArgumentException("empty", "message");
this.Severity = severity;
this.Message = message;
this.Exception = exception;
}
}
За бажанням, цю абстракцію можна розширити за допомогою деяких простих методів розширення (що дозволяє інтерфейсу залишатися вузьким і дотримуватися Інтернету). Це робить код для споживачів цього інтерфейсу набагато простішим:
public static class LoggerExtensions
{
public static void Log(this ILogger logger, string message) {
logger.Log(new LogEntry(LoggingEventType.Information, message));
}
public static void Log(this ILogger logger, Exception exception) {
logger.Log(new LogEntry(LoggingEventType.Error, exception.Message, exception));
}
// More methods here.
}
Оскільки інтерфейс містить лише один метод, ви можете легко створити ILogger
реалізацію, яка проксі-сервери log4net , Serilog , Microsoft.Extensions.Logging , NLog або будь-яку іншу бібліотеку ведення журналів та налаштувати свій контейнер DI, щоб вводити його в класи, які мають ILogger
у своєму конструктор.
Зверніть увагу, що наявність методів статичного розширення поверх інтерфейсу з одним методом цілком відрізняється від інтерфейсу з багатьма членами. Методи розширення - це лише допоміжні методи, які створюють LogEntry
повідомлення та передають його через єдиний метод на ILogger
інтерфейсі. Методи розширення стають частиною коду споживача; не є частиною абстракції. Це не тільки дозволяє методам розширення еволюціонувати без необхідності змінювати абстракцію, методи розширення таLogEntry
конструктор завжди виконується, коли використовується абстракція реєстратора, навіть коли цей реєстратор заглушений / знущається. Це дає більшу впевненість у правильності викликів реєстратора під час запуску в наборі тестів. Одночленний інтерфейс також значно полегшує тестування; Наявність абстракції з багатьма членами ускладнює створення реалізацій (таких як макети, адаптери та декоратори).
Коли ви робите це, навряд чи колись виникає потреба в якійсь статичній абстракції, яку можуть запропонувати фасади реєстрації (або будь-яка інша бібліотека).