Размышляя о гибкой разработке программного обеспечения и всех принципах (SRP, OCP, ...), я спрашиваю себя, как относиться к ведению журнала.
Является ли регистрация рядом с реализацией нарушением SRP?
Я бы сказал, yes
потому что реализация должна быть в состоянии работать без регистрации. Итак, как я могу реализовать ведение журнала лучше? Я проверил некоторые шаблоны и пришел к выводу, что лучший способ не нарушать принципы определенным пользователем способом, а использовать любой шаблон, который, как известно, нарушает принцип, состоит в использовании шаблона декоратора.
Допустим, у нас есть куча компонентов полностью без нарушения SRP, а затем мы хотим добавить ведение журнала.
- компонент А
- компонент B использует A
Нам нужна регистрация для A, поэтому мы создаем еще один компонент D, украшенный A и реализующий интерфейс I.
- интерфейс I
- компонент L (компонент регистрации системы)
- компонент А реализует I
- компонент D реализует I, украшает / использует A, использует L для регистрации
- Компонент B использует I
Преимущества: - я могу использовать A без регистрации - тестирование означает, что мне не нужны никакие журналы - тесты проще
Недостаток: - больше компонентов и больше тестов
Я знаю, что это, кажется, еще один вопрос открытой дискуссии, но на самом деле я хочу знать, использует ли кто-то лучшие стратегии ведения журналов, чем декоратор или нарушение SRP. Как насчет статического одноэлементного регистратора, который по умолчанию является NullLogger, и если требуется ведение журнала syslog, можно изменить объект реализации во время выполнения?
источник
Ответы:
Да, это нарушение SRP, поскольку ведение журнала является сквозной проблемой.
Правильный способ - делегировать ведение журнала классу ведения журнала (Interception), единственной целью которого является ведение журнала с помощью SRP.
Смотрите эту ссылку для хорошего примера: https://msdn.microsoft.com/en-us/library/dn178467%28v=pandp.30%29.aspx
Вот краткий пример :
Преимущества включают
источник
TenantStoreLogger
изменения будут меняться каждый разTenantStore
. Вы не разделяете проблемы больше, чем в первоначальном решении.Я бы сказал, что вы слишком серьезно относитесь к SRP. Если ваш код достаточно аккуратен, что регистрация является единственным «нарушением» SRP, то вы справляетесь лучше, чем 99% всех других программистов, и вам следует погладить себя по пятам.
Смысл SRP состоит в том, чтобы избежать ужасного спагетти-кода, когда код, который выполняет разные вещи, все смешивается вместе. Смешивание логирования с функциональным кодом не вызывает у меня тревоги.
источник
Do you mock the logger?
, это ТОЧНО, что вы делаете. У вас должен бытьILogger
интерфейс, который определяет, что делает регистратор. Тестируемый код вставляется с указаннымILogger
вами. Для тестирования у вас естьclass TestLogger : ILogger
. Самое замечательное в этом - этоTestLogger
возможность выставлять такие вещи, как последняя строка или записанная ошибка. Тесты могут проверить, что тестируемый код регистрирует правильно. Например, тест может бытьUserSignInTimeGetsLogged()
, где тест проверяется наTestLogger
наличие в журнале.Нет, это не нарушение ПСП.
Сообщения, которые вы отправляете в журнал, должны меняться по тем же причинам, что и окружающий код.
Что является нарушением SRP, так это использование специальной библиотеки для регистрации непосредственно в коде. Если вы решите изменить способ ведения журнала, SRP заявляет, что это не должно влиять на ваш бизнес-код.
Какой-то реферат
Logger
должен быть доступен для кода вашей реализации, и единственное, что ваша реализация должна сказать, это «Отправить это сообщение в журнал», не беспокоясь о том, как это делается. Принятие решения о точном способе регистрации (даже отметки времени) не входит в обязанности вашей реализации.Тогда ваша реализация также не должна знать, является ли регистратор, которому она отправляет сообщения
NullLogger
.Это сказал.
Я не стал бы отмахиваться от лесозаготовок, потому что это слишком быстро . Создание журналов для отслеживания определенных событий, происходящих в коде реализации, относится к коду реализации.
OTOH - это сквозная проблема, связанная с отслеживанием выполнения : ведение журнала входит и выходит в каждом методе. АОП лучше всего подходит для этого.
источник
Login
-интерфейс, украшенный тем же регистратором.Поскольку ведение журнала часто считается сквозной задачей, я бы предложил использовать АОП для отделения ведения журнала от реализации.
В зависимости от языка вы используете для этого перехватчик или некоторую инфраструктуру AOP (например, AspectJ в Java).
Вопрос в том, стоит ли это на самом деле хлопот. Обратите внимание, что это разделение увеличит сложность вашего проекта, предоставляя при этом очень мало преимуществ.
источник
Это звучит нормально. Вы описываете довольно стандартный логаринг-декоратор. У тебя есть:
Это имеет одну ответственность: регистрация информации, которая передается ему.
Это имеет одну ответственность: обеспечение реализации интерфейса I (при условии, что я должным образом совместим с SRP, то есть).
Это важная часть:
Когда это так, это звучит сложно, но посмотрите на это так: Компонент D делает одну вещь: объединяя A и L вместе.
Только ответственность , что компонент D имеет, чтобы убедиться , что L уведомляется , когда А используется. Реализации A и L оба в другом месте. Это полностью SRP-совместимый, а также является отличным примером OCP и довольно распространенным использованием декораторов.
Важное предостережение: когда D использует ваш компонент журналирования L, он должен делать это таким образом, чтобы вы могли изменить способ ведения журнала. Самый простой способ сделать это - иметь интерфейс IL, который реализуется L. Затем:
Таким образом, ничто не зависит напрямую от чего-либо другого, что облегчает их обмен. Это облегчает адаптацию к изменениям и позволяет имитировать части системы, чтобы можно было проводить модульное тестирование.
источник
D implements I
. Спасибо за ваш ответ.Конечно, это нарушение SRP, поскольку у вас есть сквозная проблема. Однако вы можете создать класс, который будет отвечать за составление журналов с выполнением любого действия.
пример:
источник