Лесорубы - это то, что мы называем «сквозной проблемой». Они уступают методам, таким как Аспектно-ориентированное Программирование; если у вас есть способ украсить свои классы атрибутом или выполнить какое-то переплетение кода, то это хороший способ получить возможности ведения журналов, сохраняя при этом ваши объекты и списки параметров «чистыми».
Единственная причина, по которой вы можете захотеть передать регистратор, состоит в том, что вы хотите указать различные реализации ведения журналов, но большинство структур ведения журналов имеют гибкость, позволяющую настраивать их, например, для разных целей ведения журнала. (файл журнала, диспетчер событий Windows и т. д.)
По этим причинам я предпочитаю делать регистрацию естественной частью системы, а не передавать регистратор в каждый класс для целей регистрации. Так что я обычно делаю ссылки на соответствующее пространство имен журналирования и просто использую регистратор в моих классах.
Если вы все еще хотите передать регистратор, я предпочитаю, чтобы вы сделали его последним параметром в списке параметров (если это возможно, сделайте его необязательным параметром). Наличие первого параметра не имеет особого смысла; первый параметр должен быть самым важным, наиболее релевантным для работы класса.
В языках с перегрузкой функций, я бы сказал, что чем больше аргумент является необязательным, тем более правильным он должен быть. Это создает согласованность, когда вы создаете перегрузки, где они отсутствуют:
В функциональных языках обратная сторона более полезна - чем выше вероятность выбора какого-либо значения по умолчанию, тем дальше должно быть. Это облегчает специализацию функции, просто применяя к ней аргументы:
Однако, как упоминалось в других ответах, вы, вероятно, не хотите явно передавать регистратор в список аргументов каждого класса в системе.
источник
Вы можете определенно потратить много времени на решение этой проблемы.
Для языков с реализациями канонического журналирования просто создайте экземпляр канонического регистратора непосредственно в каждом классе.
Для языков без канонической реализации, попробуйте найти каркас фасада регистрации и придерживаться его. slf4j - хороший выбор в Java.
Лично я предпочел бы придерживаться одной конкретной реализации регистрации и отправить все в системный журнал. Все хорошие инструменты анализа журналов способны объединять журналы sysout с нескольких серверов приложений в единый отчет.
Когда сигнатура функции включает в себя одну или две службы зависимостей, а также некоторые «реальные» аргументы, я ставлю зависимости в последнюю очередь:
int calculateFooBarSum(int foo, int bar, IntegerSummationService svc)
Поскольку мои системы обычно имеют только пять или менее таких служб, я всегда проверяю, чтобы службы были включены в один и тот же порядок для всех сигнатур функций. Алфавитный порядок так же хорош, как и любой. (Кроме того: поддержание этого методологического подхода для обработки мьютексов также снизит ваши шансы на развитие взаимоблокировок.)
Если вы обнаружите, что внедряете в свое приложение более дюжины или около того зависимостей, то систему, вероятно, нужно разделить на отдельные подсистемы (смею сказать, микросервисы?).
источник
Регистраторы - это особый случай, потому что они должны быть доступны буквально везде.
Если вы решили, что хотите передать регистратор в конструктор каждого класса, то вам определенно следует установить согласованное соглашение о том, как вы это делаете (например, всегда первый параметр, всегда передаваемый по ссылке, список инициализации конструктора всегда запускается с m_logger (theLogger) и т. д.). Все, что будет использоваться во всей вашей кодовой базе, когда-нибудь получит пользу от согласованности.
В качестве альтернативы, вы можете сделать так, чтобы каждый класс создавал свой собственный объект logger, без необходимости что-либо передавать. Регистратору может понадобиться знать некоторые вещи «по волшебству», чтобы это работало, но жесткое кодирование пути к файлу в определении класса потенциально Гораздо более легок в обслуживании и менее утомителен, чем правильная передача его сотням различных классов, и, возможно, гораздо менее злым, чем использование глобальной переменной для обхода упомянутой утомительной работы (Следует признать, что регистраторы являются одним из немногих законных вариантов использования глобальных переменных)
источник
Я согласен с теми, кто предполагает, что логгер должен быть статически доступен, а не передан в классы. Однако, если есть веская причина, по которой вы хотите передать его (возможно, разные экземпляры захотят войти в другое место или что-то в этом роде), я бы посоветовал вам не передавать его с помощью конструктора, а сделать отдельный вызов для этого, например,
Class* C = new C(); C->SetLogger(logger);
скорее чемClass* C = new C(logger);
Причина, по которой этот метод является предпочтительным, заключается в том, что средство ведения журнала является не частью класса, а скорее внедренной функцией, используемой для каких-то других целей. Размещение его в списке конструктора делает его обязательным для класса и подразумевает, что оно является частью фактического логического состояния класса. Например, разумно ожидать (с большинством классов, хотя и не со всеми), что если
X != Y
тогда,C(X) != C(Y)
но маловероятно, что вы протестируете неравенство регистратора, если будете сравнивать слишком много экземпляров одного и того же класса.источник
Стоит упомянуть, что я не видел, чтобы другие ответы касались здесь, что ввод логгера с помощью свойства или статики затрудняет (э) тестирование класса модулем. Например, если вы вводите свой регистратор через свойство, теперь вам придется вводить этот регистратор каждый раз, когда вы тестируете метод, который использует регистратор. Это означает, что вы могли бы также установить его как конструкторскую зависимость, потому что классу это требуется.
Статика поддается той же проблеме; если регистратор не работает, то весь ваш класс выходит из строя (если ваш класс использует регистратор) - даже если регистратор не обязательно является «частью» ответственности класса - хотя это не так плохо, как внедрение свойства, потому что вы по крайней мере, знать, что регистратор всегда "там" в некотором смысле.
Просто пища для размышлений, особенно если вы используете TDD. По моему мнению, логгер не должен быть частью тестируемой части класса (когда вы тестируете класс, вам также не следует тестировать логирование).
источник
Мне лень передавать объект logger каждому экземпляру класса. Итак, в моем коде такие вещи находятся либо в статическом поле, либо в локальной переменной потока в статическом поле. Последнее довольно круто и позволяет вам использовать разные средства ведения журнала для каждого потока и позволяет добавлять методы для включения и выключения регистрации, которые делают что-то значимое и ожидаемое в многопоточном приложении.
источник