Создание объектов с зависимостями - внедрение зависимостей - PullRequest
4 голосов
/ 17 ноября 2011

Допустим, у нас есть класс:

public class WithDependencies
{
  public WithDependencies(IAmDependencyOne first, IAmDependencyTwo second)
  // ...
}

Теперь вопрос.Как вы создаете объекты класса WithDependencies в приложении?Я знаю, что есть много способов.

<code>new WithDependencies(new DependencyOne(), new DependencyTwo());
<code>new WithDependencies(IoC.Resolve(IDependencyOne), IoC.Resolve(IDependencyTwo());
<code>// register IDependencyOne, IDependencyTwo implementations at app start
IoC.Resolve(WithDependencies);
<code>// register IDependencyOne, IDependencyTwo implementations at app start
// isolate ourselves from concrete IoC Container
MyCustomWithDependenciesFactory.Create();

и так далее ...

Как вы думаете, как это сделать?

Редактировать:

Поскольку я не получаю ответы или не понимаю их, я постараюсь спроситьснова.Допустим, что для какого-то события (кнопка, таймер, что угодно) мне нужен новый объект WithDependencies ().Как мне его создать?Предположим, контейнер IoC уже настроен.

Ответы [ 3 ]

3 голосов
/ 17 ноября 2011

Это зависит от контекста, поэтому невозможно дать один ответ. Концептуально вы бы делали что-то подобное из Composition Root :

var wd = new WithDependencies(new DependencyOne(), new DependencyTwo());

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

var dep1 = new DependencyOne();
var wd = new WithDependencies(dep1, new DependencyTwo());
var another = AnotherWithDependencies(dep1, new DependencyThree());

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

Это всего лишь небольшой проблеск всего измерения DI, связанного с Lifetime Management . Многие DI-контейнеры могут позаботиться об этом за вас, что является отличным аргументом, чтобы предпочесть DI-контейнер, а не DI бедняков.

После того, как вы начнете использовать DI-контейнер, вы должны следовать Зарегистрировать шаблон разрешения восстановления при разрешении типов, позволяя Auto-wire позаботиться о фактической композиции:

var wd = container.Resolve<WithDependencies>();

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

2 голосов
/ 18 ноября 2011

Если вам нужно создать зависимость, которая имеет свои зависимости, вы можете либо A) сделать это самостоятельно, либо B) попросить кого-то еще сделать это для вас.Вариант A сводит на нет преимущества внедрения зависимостей (развязки и т. Д.), Поэтому я бы сказал, что вариант B является лучшей отправной точкой.Теперь мы решили использовать шаблон фабрики, независимо от того, принимает ли он форму сервисного локатора (то есть IoC.Resolve), статической фабрики или фабрики экземпляров.Дело в том, что мы передали эту ответственность внешнему органу.

Для статических методов доступа требуется ряд компромиссов.( Я рассмотрел их в другом ответе , поэтому я не буду повторять их здесь.) Чтобы избежать зависимости от инфраструктуры или контейнера, надежным вариантом является принятие фабрики для создания WithDependencies когда нам нужен экземпляр где-то еще:

public class NeedsWithDependencies
{
    private readonly IWithDependenciesFactory _withDependenciesFactory;

    public NeedsWithDependencies(IWithDependenciesFactory withDependenciesFactory)
    {
        _withDependenciesFactory = withDependenciesFactory;
    }

    public void Foo()
    {
        var withDependencies = _withDependenciesFactory.Create();

        ...Use the instance...
    }
}

Далее мы можем создать контейнерную реализацию фабрики:

public class WithDependenciesFactory : IWithDependenciesFactory
{
    private readonly IContainer _container;

    public WithDependenciesFactory(IContainer container)
    {
        _container = container
    }

    public WithDependencies Create()
    {
        return _container.Resolve<WithDependencies>();
    }
}

Теперь NeedsWithDependencies полностью изолирован отлюбые знания о том, как создается WithDependencies;он также раскрывает все свои зависимости в своем конструкторе, вместо того, чтобы скрывать зависимости от статических методов доступа, упрощая его повторное использование и тестирование.

Однако определение всех этих фабрик может стать немного громоздким.Мне нравится тип фабричных отношений Autofac , который определяет параметры формы Func<TDependency> и автоматически вводит функцию, которая выполняет ту же цель, что и фабрика с ручным кодированием, указанная выше:

public class NeedsWithDependencies
{
    private readonly Func<WithDependencies> _withDependenciesFactory;

    public NeedsWithDependencies(Func<WithDependencies> withDependenciesFactory)
    {
        _withDependenciesFactory = withDependenciesFactory;
    }

    public void Foo()
    {
        var withDependencies = _withDependenciesFactory();

        ...Use the instance...
    }
}

Он также отлично работает с параметрами времени выполнения:

public class NeedsWithDependencies
{
    private readonly Func<int, WithDependencies> _withDependenciesFactory;

    public NeedsWithDependencies(Func<int, WithDependencies> withDependenciesFactory)
    {
        _withDependenciesFactory = withDependenciesFactory;
    }

    public void Foo(int x)
    {
        var withDependencies = _withDependenciesFactory(x);

        ...Use the instance...
    }
}
0 голосов
/ 17 ноября 2011

Иногда я пытаюсь избавиться от фабрик или, по крайней мере, не зависеть напрямую от них, поэтому, конечно, полезно внедрение зависимостей (без фабрик).

Поэтому я использую Google Juice, потому что это маленький маленький фреймворкиспользуя аннотации Java, и вы можете быстро изменить ваши инъекции / зависимости.Просто взгляните на это:

http://code.google.com/p/google-guice/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...