Внедрение зависимостей в сравнении с шаблоном фабрики - PullRequest
463 голосов
/ 17 февраля 2009

Большинство примеров, приведенных для использования Dependency Injection, мы также можем решить, используя фабричный шаблон. Похоже, что когда дело доходит до использования / дизайна, разница между внедрением зависимости и фабрикой размыта или тонка.

Однажды кто-то сказал мне, что то, как вы его используете, имеет значение!

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

Может кто-нибудь сказать мне, в чем разница между ними и где что использовать, какова лучшая практика здесь?

Ответы [ 27 ]

280 голосов
/ 17 февраля 2009

При использовании фабрики ваш код по-прежнему фактически отвечает за создание объектов. Под DI вы передаете эту ответственность другому классу или фреймворку, который отделен от вашего кода.

210 голосов
/ 17 февраля 2009

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

169 голосов
/ 04 июня 2013

Внедрение зависимостей

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

class Car
{
    private Engine engine;
    private SteeringWheel wheel;
    private Tires tires;

    public Car(Engine engine, SteeringWheel wheel, Tires tires)
    {
        this.engine = engine;
        this.wheel = wheel;
        this.tires = tires;
    }
}

Завод

Собирает части воедино, чтобы получить законченный объект, и скрывает тип бетона от вызывающей стороны.

static class CarFactory
{
    public ICar BuildCar()
    {
        Engine engine = new Engine();
        SteeringWheel steeringWheel = new SteeringWheel();
        Tires tires = new Tires();
        ICar car = new RaceCar(engine, steeringWheel, tires);
        return car;
    }   
}

Результат

Как видите, фабрики и DI дополняют друг друга.

static void Main()
{
     ICar car = CarFactory.BuildCar();
     // use car
}

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

void RaceCar() // example #1
{
    ICar car = CarFactory.BuildCar();
    car.Race();
}

void RaceCar(ICarFactory carFactory) // example #2
{
    ICar car = carFactory.BuildCar();
    car.Race();
}

void RaceCar(ICar car) // example #3
{
    car.Race();
}

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

Пример # 2 - Это немного лучше, потому что теперь мы знаем, что нам нужна машина, так как мы проезжаем на автомобильном заводе. Но на этот раз мы проходим слишком много, так как все, что нужно на самом деле, - это машина. Мы проезжаем по фабрике только для того, чтобы построить машину, когда машину можно построить вне метода и передать.

Пример # 3 - Это идеально, потому что метод запрашивает в точности , что ему нужно. Не слишком много или слишком мало. Мне не нужно писать MockCarFactory только для создания MockCars, я могу передать макет прямо. Он прямой, а интерфейс не лжёт.

Этот Google Tech Talk от Misko Hevery удивителен и является основой того, из чего я получил свой пример. http://www.youtube.com/watch?v=XcT4yYu_TTs

39 голосов
/ 17 февраля 2009

Есть проблемы, которые легко решить с помощью внедрения зависимостей, которые не так легко решить с помощью набора фабрик.

Некоторое различие между, с одной стороны, инверсией управления и внедрением зависимостей (IOC / DI) и, с другой стороны, сервисным локатором или набором фабрик (фабрика), составляет:

IOC / DI - это полноценная экосистема доменных объектов и сервисов сама по себе. Он настраивает все для вас так, как вы указываете. Ваши доменные объекты и сервисы создаются контейнером и не конструируются сами собой: поэтому они не имеют каких-либо зависимостей от контейнера или каких-либо фабрик. IOC / DI обеспечивает чрезвычайно высокую степень конфигурируемости, при этом вся конфигурация находится в одном месте (конструкция контейнера) на самом верхнем уровне вашего приложения (GUI, веб-интерфейс).

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

36 голосов
/ 13 марта 2014

Причина, по которой внедрение зависимостей (DI) и фабричные шаблоны похожи, заключается в том, что они являются двумя реализациями Inversion of Control (IoC), представляющими собой программную архитектуру. Проще говоря, это два решения одной и той же проблемы.

Таким образом, чтобы ответить на вопрос, основное различие между шаблоном Factory и DI заключается в том, как получается ссылка на объект. С внедрением зависимости, как следует из названия, ссылка внедряется или передается вашему коду. С шаблоном Factory ваш код должен запрашивать ссылку, чтобы ваш код выбирал объект. Обе реализации удаляют или разъединяют связь между кодом и базовым классом или тип ссылки на объект, используемый кодом.

Стоит отметить, что шаблоны фабрики (или действительно шаблоны абстрактной фабрики, представляющие собой фабрики, которые возвращают новые фабрики, возвращающие ссылки на объекты) могут быть записаны для динамического выбора или ссылки на тип или класс объекта, запрашиваемого во время выполнения. Это делает их очень похожими (даже в большей степени, чем DI) на шаблон Service Locator, который является еще одной реализацией IoC.

Шаблон проектирования фабрики довольно стар (с точки зрения программного обеспечения) и существует уже некоторое время. С недавней популярности архитектурного паттерна IoC он возрождается.

Я предполагаю, что когда дело доходит до шаблонов проектирования IoC: инжекторы вводят, локаторы располагаются, а заводы реорганизуются.

24 голосов
/ 01 мая 2012

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

21 голосов
/ 20 декабря 2010

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

От: http://tutorials.jenkov.com/dependency-injection/dependency-injection-containers.html

16 голосов
/ 17 февраля 2009

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

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

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

12 голосов
/ 13 сентября 2012

Я знаю, что этот вопрос старый, но я хотел бы добавить свои пять центов,

Я думаю, что внедрение зависимостей (DI) во многом похоже на конфигурируемый Factory Pattern (FP), и в этом смысле все, что вы можете сделать с DI, вы сможете сделать с такой фабрикой.

На самом деле, если вы используете Spring, например, у вас есть возможность автоматического подключения ресурсов (DI) или делать что-то вроде этого:

MyBean mb = ctx.getBean("myBean");

А затем используйте этот экземпляр 'mb', чтобы сделать что-нибудь. Разве это не вызов фабрике, которая вернет вам экземпляр?

Единственное реальное различие, которое я замечаю между большинством примеров FP, это то, что вы можете настроить, что «myBean» находится в xml или в другом классе, и фреймворк будет работать как фабрика, но в остальном это то же самое и, конечно, у вас может быть Фабрика, которая читает файл конфигурации или получает реализацию по мере необходимости.

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

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

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

и, наконец, не имеет значения, сколько DI-фреймворк обещает , что вы будете строить из него вещи отделенные без каких-либо зависимостей от их классов, если вы используете фреймворк, вы строите все вокруг, , если вам нужно изменить подход или фреймворк, это не будет легкой задачей ... КОГДА-ЛИБО! ... но, поскольку вы строите все вокруг этого Если вы не будете беспокоиться о том, какое решение лучше всего подходит для вашего бизнеса, вы столкнетесь с серьезной проблемой при этом.

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

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

MyBean mb = MyBeanForEntreprise1(); //In the classes of the first enterprise
MyBean mb = MyBeanForEntreprise2(); //In the classes of the second enterprise

так же, как это:

@Autowired MyBean mbForEnterprise1; //In the classes of the first enterprise
@Autowired MyBean mbForEnterprise2; //In the classes of the second enterprise

И это:

MyBean mb = (MyBean)MyFactory.get("myBeanForEntreprise1"); //In the classes of the first enterprise
MyBean mb = (MyBean)MyFactory.get("myBeanForEntreprise2"); //In the classes of the second enterprise

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

Не было бы неплохо сделать что-то вроде этого:

MyBean mb = (MyBean)MyFactory.get("mb"); 

И таким образом, вы устанавливаете код фабрики, чтобы получить правильную реализацию во время выполнения в зависимости от зарегистрированного пользователя предприятия ?? Теперь это будет полезно. Вы можете просто добавить новый jar с новыми классами и установить правила, возможно, даже во время выполнения (или добавить новый файл конфигурации, если вы оставите эту опцию открытой), без изменений в существующих классах. Это будет динамическая фабрика!

Разве это не было бы более полезным, чем писать две конфигурации для каждого предприятия и, возможно, даже иметь два разных приложения для каждого ??

Вы можете сказать мне, что мне вообще не нужно переключаться во время выполнения, поэтому я настраиваю приложение, и, если я наследую класс или использую другую реализацию, я просто изменяю конфигурацию и повторно развертываю. Хорошо, это также может быть сделано с завода. И, честно говоря, сколько раз вы делаете это? возможно, только когда у вас есть приложение, которое будет использоваться где-то еще в вашей компании, и вы собираетесь передать код другой команде, и они будут делать такие вещи. Но эй, это также можно сделать с фабрикой, и было бы еще лучше с динамической фабрикой !!

В любом случае, секция комментариев открыта для вас, чтобы убить меня.

12 голосов
/ 29 мая 2017

Теория

Необходимо учитывать два важных момента:

  1. Кто создает объекты

    • [Factory]: вам нужно написать, КАК объект должен быть создан. У вас есть отдельный класс Factory, который содержит логику создания.
    • [Внедрение зависимости]: В практических случаях это делается внешними фреймворками (например, в Java это будет spring / ejb / guice). Инъекция происходит «магическим образом» без явного создания новых объектов
  2. Какими объектами он управляет:

    • [Factory]: обычно отвечает за создание объектов с состоянием
    • [Инъекции зависимостей] Более вероятно создание объектов без состояния

Практический пример использования фабричной и зависимой инъекций в одном проекте

  1. Что мы хотим построить

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

  1. Архитектура

Предположим, мы хотим создать следующую архитектуру слоя:

enter image description here

Доменные объекты могут быть объектами, хранящимися в базе данных. Репозиторий (DAO) помогает с извлечением объектов из базы данных. Сервис предоставляет API для других модулей. Допускает операции на order модуле

  1. Доменный уровень и использование фабрик

Объектами, которые будут в базе данных, являются Order и OrderLine. Заказ может иметь несколько строк заказа. Relationship between Order and OrderLine

Теперь важная часть дизайна. Должны ли модули за пределами этого создавать и управлять OrderLines самостоятельно? Нет. Строка заказа должна существовать только тогда, когда с ней связан Заказ. Было бы лучше, если бы вы могли скрыть внутреннюю реализацию от внешних классов.

Но как создать Order без знания OrderLines?

Завод

Кто-то, кто хочет создать новый заказ, использовал OrderFactory (который скрывает подробности о том, как мы создаем заказ).

enter image description here

Вот как это будет выглядеть внутри IDE. Классы вне пакета domain будут использовать OrderFactory вместо конструктора внутри Order

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

OrderRepository и OrderService управляются инфраструктурой внедрения зависимостей. Репозиторий отвечает за управление операциями CRUD над базой данных. Служба внедряет репозиторий и использует его для сохранения / поиска правильных классов домена.

enter image description here

...