Какие зависимости я должен вводить? - PullRequest
6 голосов
/ 09 ноября 2010

При использовании внедрения зависимостей, какие зависимости вы вводите?

Я ранее вводил все зависимости, но обнаружил, что при выполнении TDD обычно есть два типа зависимостей:

  • Те, которыеподлинные внешние зависимости, которые могут изменяться, например ProductRepository
  • Те, которые существуют исключительно для тестируемости, например, часть поведения класса, который был извлечен и введен только для тестируемости

Один из подходовчтобы внедрить ВСЕ зависимости как это

public ClassWithExternalDependency(IExternalDependency external,
    IExtractedForTestabilityDependency internal)
{
    // assign dependencies ...
}

, но я обнаружил, что это может вызвать раздувание зависимостей в реестре DI.

Другой подход состоит в том, чтобы скрыть «зависимость тестируемости» как это

public ClassWithExternalDependency(IExternalDependency external)
    : this (external, new ConcreteClassOfInternalDependency())
{}

internal ClassWithExternalDependency(IExternalDependency external,
    IExtractedForTestabilityDependency internal)
{
    // assign dependencies ...
}

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

Какой подход вы бы поддержали и почему?

Ответы [ 3 ]

1 голос
/ 09 ноября 2010

Полагаю, вам лучше вводить все свои зависимости.Если он начинает становиться немного громоздким, это, вероятно, указывает на то, что вам нужно немного упростить вещи или переместить зависимости в другой объект.Ощущение «боли» вашего дизайна на ходу может быть действительно поучительным.

Что касается раздувания зависимостей в реестре, вы можете рассмотреть возможность использования какого-либо традиционного метода связывания вместо регистрации каждой зависимости вручную.Некоторые контейнеры IoC имеют встроенные привязки сканирования типов.Например, вот часть модуля, который я использую в приложении Caliburn WPF, которое использует Ninject:

public class AppModule : NinjectModule
{
    public override void Load()
    {
        Bind<IShellPresenter>().To<ShellPresenter>().InSingletonScope();

        BindAllResults();
        BindAllPresenters();
    }

    /// <summary>
    /// Automatically bind all presenters that haven't already been manually bound
    /// </summary>
    public void BindAllPresenters()
    {
        Type[] types = Assembly.GetExecutingAssembly().GetTypes();

        IEnumerable<Type> presenterImplementors =
            from t in types
            where !t.IsInterface
            && t.Name.EndsWith("Presenter")
            select t;

            presenterImplementors.Run(
                implementationType =>
                    {
                        if (!Kernel.GetBindings(implementationType).Any())
                            Bind(implementationType).ToSelf();
                    });
    }

Несмотря на то, что у меня бегают десятки результатов и докладчиков, мне не нужно регистрировать их явно.

0 голосов
/ 02 июня 2011

Я бы связывал все свои внешние внешние зависимости вручную и «регистрировал» только внешние зависимости.Когда я говорю «не внешние», я имею в виду объекты, которые принадлежат моему компоненту и которые были извлечены из интерфейсов только ради единственной ответственности / тестируемости. У меня никогда не было бы никаких других реализаций таких интерфейсов.Внешние зависимости - это такие вещи, как соединения с БД, веб-сервисы, интерфейсы, которые не принадлежат моему компоненту.Я бы зарегистрировал их как интерфейсы, потому что их реализации могут быть переключены на заглушки для интеграционного тестирования.Небольшое количество компонентов, зарегистрированных в контейнере DI, делает код DI более простым для чтения и раздувания.

0 голосов
/ 10 ноября 2010

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

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

...