Когда и где вызывать фабрики во время выполнения? - PullRequest
6 голосов
/ 22 октября 2011

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

Но что, если я не хочу создавать все экземпляры заранее?У меня есть объект, который содержит список других объектов, которым он может делегировать, но они дорогие и используются по одному, поэтому я создаю их при необходимости и позволяю собирать их, когда я закончу.Я не хочу помещать new B() в логику A, потому что я бы предпочел использовать DI - но как?Может ли A позвонить на завод?Это не выглядит намного лучше, если только фабрика не поддерживает состояние, включая текущие зависимости.Я просто не хочу передавать полный список B в A при его создании, так как это было бы расточительно.Если хотите, B не обязательно должен иметь внутри A, хотя это логично (A - это игровой уровень, B - это один экран), но вв любом случае логика A диктует, когда создается B.

Итак, кто вызывает фабрику, чтобы получить B, и когда?

Разъяснение: Я не использую рамки для DI.Интересно, подразумевает ли это термин DI?

Ответы [ 2 ]

6 голосов
/ 22 октября 2011

В Ninject вы можете зарегистрировать Func<B> и запросить в конструкторе значение A.

Autofac автоматически предоставит Func<B>, если B уже зарегистрирован.

Или вы можете выбрать более прямой подход и определить явную фабрику для B и запросить эту фабрику в конструкторе; это просто больше ввода, так как вам нужно будет создать фабрику для каждой зависимости, которую вы хотите лениво инициализировать.


Вот еще один SO-ответ, который показывает методы фабрики стилей Ninject: Как мне обрабатывать классы статическими методами с помощью Ninject?


@ Не используется Framework : Если вы можете, я, вероятно, рассмотрю его использование: платформа IoC / DI обычно обрабатывает отложенное создание для вас из коробки.

Если вы хотите продолжать катиться самостоятельно, просто передайте фабрику, которая создает B, вашему объекту A. Или, если вам просто не нравятся необработанные Funcs и вы не хотите создавать явные фабрики для всех ваших объектов, вы можете использовать Lazy<B> для более формализованного решения.

1 голос
/ 22 октября 2011

Как правило, есть два шаблона для использования редко необходимых объектов, которые дорого создавать. Первый шаблон использует фабрику, как предлагает Дэвид Фэйвр. Другой - с использованием прокси.

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

Вот простой пример с некоторым интерфейсом и дорогой реализацией:

interface IAmAService
{
    void DoSomething();
}

class ExpensiveImpl : IAmAService
{
    private object x = [some expensive init];

    void IAmAService.DoSomething() { }
}

Нет, вы можете реализовать прокси на основе этого интерфейса, который может задержать создание этой реализации:

class ServiceProxy : IAmAService
{
    private readonly Func<IAmAService> factory;
    private IAmAService instance;

    public ServiceProxy(Func<IAmAService> factory)
    {
        this.factory = factory;
    }

    void IAmAService.DoSomething()
    {
        this.GetInstance().DoSomething();
    }

    private IAmAService GetInstance()
    {
        // TODO: Implement double-checked lock only a single
        // instance may exist per ServiceProxy.
        if (this.instance == null)
        {
            this.instance = this.factory();
        }

        return this.instance;
    }
}

Этот прокси-класс принимает делегата фабрики в качестве зависимости, как описал Дэвид Фэйвр в своем ответе, но в этом случае приложение не должно зависеть от Func<IAmAService>, а может просто зависеть от IAmAService.

Теперь вместо введения ExpensiveImpl вы можете добавить ServiceProxy в другие случаи:

// Create the proxy
IAmAService service =
    new ServiceProxy(() => new ExpensiveImpl());

// Inject it into whatever you wish, such as:
var customerService = new CustomerService(service);
...