Какой шаблон использовать для входа? Внедрение зависимости или Сервисный Локатор?

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

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. Передайте его с помощью Внедрения зависимости на конструкторе.
  2. Получите его через одноэлементный Сервисный Локатор.

Какой метод Вы предпочли бы, и почему? Или есть ли еще лучший шаблон?

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

34
задан CodingInsomnia 21 April 2010 в 12:51
поделиться

8 ответов

Лично я использую и то, и другое.

Вот мои соглашения:

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

Я считаю, что это дает мне правильный баланс тестируемости. Мне немного сложнее настроить тесты для классов, использующих 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. Таким образом, мне не нужно дважды выполнять сопоставление типов или что-то в этом роде.

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

15
ответ дан 27 November 2019 в 17:09
поделиться

Мое небольшое практическое правило:

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

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

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

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

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

5
ответ дан 27 November 2019 в 17:09
поделиться

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

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

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

Проверка out this .

4
ответ дан 27 November 2019 в 17:09
поделиться

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

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

7
ответ дан 27 November 2019 в 17:09
поделиться

Вы можете получить другой тип, например 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);
        }
    }
}
2
ответ дан 27 November 2019 в 17:09
поделиться

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

0
ответ дан 27 November 2019 в 17:09
поделиться

Я бы предпочел одиночную службу.

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

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

2
ответ дан 27 November 2019 в 17:09
поделиться

Д. Я бы здесь отлично поработал. Еще одна вещь, на которую стоит обратить внимание, - это AOP .

0
ответ дан 27 November 2019 в 17:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: