Какой шаблон использовать для регистрации? Внедрение зависимостей или поиск сервисов? - PullRequest
34 голосов
/ 21 апреля 2010

Рассмотрим этот сценарий. У меня есть бизнес-логика, которая время от времени будет требоваться для записи в журнал.

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? Я вижу две основные возможности;

  1. Передайте его с помощью Dependency Injection на конструкторе.
  2. Получите его через одноразовый сервисный локатор.

Какой метод вы бы предпочли и почему? Или есть еще лучший образец?

Обновление: Обратите внимание, что мне не нужно регистрировать ВСЕ вызовы методов. Я только хочу зарегистрировать несколько (редких) событий, которые могут или не могут произойти в моем методе.

Ответы [ 9 ]

15 голосов
/ 21 апреля 2010

Я лично делаю смесь обоих.

Вот мои условные обозначения:

  • Из статического контекста - Расположение службы
  • Из контекста экземпляра - Внедрение зависимостей

Я чувствую, что это дает мне правильный баланс тестируемости. Мне немного сложнее настроить тесты для классов, которые используют Service Location, чем для DI, поэтому именно поэтому Service Location оказывается скорее исключением, чем правилом. Однако я последовательн в его использовании, поэтому нетрудно вспомнить, какой тип теста мне нужно написать.

Некоторые высказывают опасение, что DI имеет тенденцию мешать конструкторам. Я не чувствую, что это проблема, но если вы чувствуете так, есть ряд альтернатив, которые используют DI, но избегают параметров конструктора. Вот список методов DI от Ninject: http://ninject.codeplex.com/wikipage?title=Injection%20Patterns

Вы обнаружите, что большинство контейнеров Inversion of Control имеют те же функции, что и Ninject. Я решил показать Ninject, потому что у них самые лаконичные образцы.

Надеюсь, это полезно.

Редактировать: Для ясности, я использую Unity и Common Service Locator . У меня есть одноэлементный экземпляр моего контейнера Unity для DI, и моя реализация IServiceLocator является просто оболочкой для этого одноэлементного контейнера Unity. Таким образом, мне не нужно делать никаких сопоставлений типов дважды или что-то в этом роде.

Я также не нахожу АОП особенно полезным, кроме отслеживания. Мне нравится ручная регистрация лучше просто для ясности. Я знаю, что большинство каркасов логирования AOP способны на оба, но мне больше не нужно первое (хлеб и масло AOP) большую часть времени. Конечно, это всего лишь личное предпочтение.

7 голосов
/ 21 апреля 2010

Регистратор, безусловно, является сервисом, от которого зависит ваша бизнес-логика, и поэтому должен рассматриваться как зависимость так же, как вы делаете с IDependency. Вставьте регистратор в ваш конструктор.

Примечание: , даже если AOP упоминается как способ ввода журналирования. Я не согласен с тем, что это решение в данном случае. AOP отлично работает для отслеживания выполнения, но никогда не будет решением для ведения журнала как части бизнес-логики.

5 голосов
/ 21 апреля 2010

Мое эмпирическое правило:

  • Если он находится в библиотеке классов, используйте инъекцию конструктора или свойства с шаблоном нулевого объекта.

  • Если оно находится в главном приложении, используйте указатель службы (или синглтон).

Я считаю, что это очень хорошо при использовании log4net. Вы не хотите, чтобы библиотеки классов обращались к вещам, которых может не быть, но в прикладной программе вы знаете, что там будет регистратор, а библиотеки, такие как log4net, основаны на шаблоне расположения службы. *

Я склонен думать о ведении журнала как о чем-то достаточно статичном, чтобы оно не нуждалось в DI. Крайне маловероятно, что я когда-либо изменю реализацию ведения журналов в приложении, тем более что каждая инфраструктура ведения журналов невероятно гибкая и легко расширяемая. Это более важно в библиотеках классов, когда ваша библиотека может понадобиться нескольким приложениям, которые уже используют разные регистраторы.

YMMV, конечно. DI - это здорово, но это не значит, что все должно быть DI'ed.

4 голосов
/ 21 апреля 2010

Мы переключили все наши атрибуты Logging / Tracing на PostSharp (AOP framework). Все, что вам нужно сделать для создания журнала для метода, это добавить к нему атрибут.

Преимущества:

  • Простота использования AOP
  • Очистить Разделение интересов
  • Происходит в время компиляции -> Минимальное влияние на производительность

Проверьте это .

3 голосов
/ 07 октября 2016

Возможно, это будет небольшим оффтопом, но зачем вообще нужно вводить логгер, когда мы можем просто напечатать в начале класса:

Logger logger = LogManager.GetLogger("MyClassName");

Регистратор не изменяется во время разработки, а затем во время обслуживания. Современные регистраторы легко настраиваются, поэтому аргумент

что если я захочу заменить текстовый логгер базой данных?

пропущено.

Я не отрицаю использование инъекций зависимостей, мне просто интересно ваше мнение.

2 голосов
/ 21 апреля 2010

Вы можете получить другой тип, например, 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);
        }
    }
}
1 голос
/ 21 апреля 2010

Я бы предпочел Singleton Service.

Внедрение зависимостей загромождает конструктор.

Если вы можете использовать АОП, это было бы лучшим.

0 голосов
/ 21 апреля 2010

Я бы не рекомендовал ни один из этих подходов. Лучше использовать аспектно-ориентированное программирование. Ведение журнала - это «привет мир» АОП.

0 голосов
/ 21 апреля 2010

D Я бы хорошо работал здесь.Еще одна вещь, на которую стоит обратить внимание: AOP .

...