Какой шаблон использовать для ведения журнала? Инъекция зависимостей или локатор обслуживания?
Рассмотрим этот сценарий. У меня есть некоторая бизнес-логика, которую время от времени потребуется писать в журнал.
interface ILogger
{
void Log(string stuff);
}
interface IDependency
{
string GetInfo();
}
class MyBusinessObject
{
private IDependency _dependency;
public MyBusinessObject(IDependency dependency)
{
_dependency = dependency;
}
public string DoSomething(string input)
{
// Process input
var info = _dependency.GetInfo();
var intermediateResult = PerformInterestingStuff(input, info);
if (intermediateResult== "SomethingWeNeedToLog")
{
// How do I get to the ILogger-interface?
}
var result = PerformSomethingElse(intermediateResult);
return result;
}
}
Как вы получите интерфейс ILogger? Я вижу две основные возможности:
- Передайте его, используя Injection Dependency в конструкторе.
- Получите его через одноранговый сервисный локатор.
Какой метод вы бы предпочли, и почему? Или есть еще лучший образец?
Update:
Обратите внимание, что мне не нужно регистрировать ВСЕ вызовы методов. Я хочу только регистрировать несколько (редких) событий, которые могут возникнуть или не произойти в моем методе.
Ответы
Ответ 1
Я лично делаю смесь обоих.
Вот мои соглашения:
- Из статического контекста - Местоположение службы
- Из контекста экземпляра - Инъекция зависимостей
Я чувствую, что это дает мне правильный баланс проверяемости. Мне немного сложнее настроить тесты на классы, которые используют Service Location, чем использовать DI, поэтому именно поэтому Service Location становится исключением, а не правилом. Я согласен с этим в использовании, поэтому не трудно запомнить, какой тип теста мне нужно написать.
Некоторые из них выразили обеспокоенность в связи с тем, что DI стремится к созданию конструкторов. Я не чувствую, что это проблема, но если вы так себя чувствуете, существует ряд альтернатив, которые используют DI, но избегайте параметров конструктора. Вот список методов Ninject DI:
http://ninject.codeplex.com/wikipage?title=Injection%20Patterns
Вы обнаружите, что большинство контейнеров Inversion of Control имеют те же функции, что и Ninject. Я решил показать Ninject, потому что у них самые сжатые образцы.
Надеюсь, это поможет.
Изменить: Чтобы быть ясным, я использую Unity и Common Service Locator. У меня есть экземпляр singleton моего контейнера Unity для DI, и моя реализация IServiceLocator - это просто обертка вокруг этого контейнера Singleton Unity. Таким образом, мне не нужно делать какие-либо сопоставления типов дважды или что-то в этом роде.
Я также не считаю, что AOP особенно полезно для отслеживания. Мне нравится ручная регистрация лучше просто для ее ясности. Я знаю, что большинство протоколов регистрации АОП способны к обоим, но я не нуждаюсь в первом (AOP-хлеб и масло) большую часть времени. Это, конечно, личное предпочтение.
Ответ 2
Логгер, безусловно, является сервисом, от которого зависит ваша бизнес-логика, и поэтому его следует рассматривать как зависимость так же, как и с IDependency
. Внесите регистратор в свой конструктор.
Примечание:, хотя AOP упоминается как способ ввода журнала. Я не согласен с тем, что это решение в этом случае. AOP отлично подходит для отслеживания выполнения, но никогда не будет решением для ведения журнала как части бизнес-логики.
Ответ 3
Мы переключили все атрибуты Logging/Tracing в PostSharp (AOP framework). Все, что вам нужно сделать для создания журнала для метода, - это добавить к нему атрибут.
Преимущества:
- Простое использование AOP
- Очистить разделение проблем
- Происходит при времени компиляции → Минимальное воздействие на производительность
Отметьте этот.
Ответ 4
Мое маленькое эмпирическое правило:
-
Если это в библиотеке классов, используйте либо инъекцию конструктора, либо инъекцию свойства с шаблоном нулевого объекта.
-
Если он используется в основном приложении, используйте локатор службы (или singleton).
Я считаю, что это очень хорошо при использовании log4net. Вы не хотите, чтобы библиотеки классов обращались к вещам, которые могут отсутствовать, но в прикладной программе вы знаете, что журнал будет там, а библиотеки, подобные log4net, в значительной степени основаны на шаблоне расположения службы.
Я склонен думать о регистрации как о чем-то достаточно статичном, что ему действительно не нужно DI. Очень маловероятно, что я когда-либо изменю реализацию каротажа в приложении, тем более, что каждая инфраструктура ведения журнала невероятно гибкая и простая в использовании. Это более важно в библиотеках классов, когда ваша библиотека, возможно, потребуется использовать несколькими приложениями, которые уже используют разные регистраторы.
YMMV, конечно. DI отлично, но это не значит, что все должно быть выполнено.
Ответ 5
Вы можете получить другой тип, например. LoggableBusinessObject
, который принимает логгер в своем конструкторе. Это означает, что вы только проходите в журнале для объектов, которые будут его использовать:
public class MyBusinessObject
{
private IDependency _dependency;
public MyBusinessObject(IDependency dependency)
{
_dependency = dependency;
}
public virtual string DoSomething(string input)
{
// Process input
var info = _dependency.GetInfo();
var result = PerformInterestingStuff(input, info);
return result;
}
}
public class LoggableBusinessObject : MyBusinessObject
{
private ILogger _logger;
public LoggableBusinessObject(ILogger logger, IDependency dependency)
: base(dependency)
{
_logger = logger;
}
public override string DoSomething(string input)
{
string result = base.DoSomething(input);
if (result == "SomethingWeNeedToLog")
{
_logger.Log(result);
}
}
}
Ответ 6
Может быть, это будет немного оффтопным, но зачем нам вообще вводить регистратор, когда мы можем просто набрать начало в классе:
Logger logger = LogManager.GetLogger("MyClassName");
Логгер не изменяется во время разработки, а затем во время обслуживания. Современные регистраторы очень настраиваемые, поэтому аргумент
Что делать, если я хочу заменить текстовый регистратор на базу данных?
пропущено.
Я не отрицаю использование инъекции зависимостей, мне просто интересно узнать о вашем уме.
Ответ 7
DI будет работать хорошо здесь. Еще одна вещь, на которую нужно обратить внимание: AOP.
Ответ 8
Я бы не рекомендовал ни один из этих подходов. Лучше использовать аспектно-ориентированное программирование. Регистрация - это "мир привет" АОП.
Ответ 9
Я бы предпочел Singleton Service.
Включение зависимостей будет загромождать конструктор.
Если вы можете использовать AOP, это было бы лучше.