dependency injection - русском - Инъекция зависимостей против заводского шаблона




внедрение зависимостей swift (18)

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

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

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

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


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

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


теория

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

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

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

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

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

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

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

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

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

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

  1. Уровень домена и использование заводов

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

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

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

завод

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

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

  1. Инъекционная инъекция зависимостей Инъекция более часто используется с не имеющими аналогов слоями, такими как репозиторий и служба.

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


Большинство ответов здесь объясняют концептуальную разницу и детали реализации обоих. Однако мне не удалось найти объяснения о различиях в применении, которые ИМО является самым важным и об этом спрашивает ОП. Поэтому позвольте мне снова открыть эту тему ...

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

Именно так. В 90% случаях вы можете получить ссылку на объект, используя Factory или DI, и обычно вы заканчиваете последним. В других 10% случаях использование Factory является правильным . Эти случаи включают в себя получение объектов по переменной во временных параметрах. Как это:

IWebClient client = factoryWithCache.GetWebClient(url: ".com",
        useCookies: false, connectionTimeout: 120);

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


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

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

Это также относится к ручному DI (т. Е. Когда нет внешней структуры, которая обеспечивает зависимости к вашим объектам, но вы передаете их в каждом конструкторе).


Инъекционная структура - это реализация Factory Pattern.

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

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

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


МОК - это концепция, которая реализуется двумя способами. Создание зависимостей и зависимость, заводская / абстрактная фабрика являются примером создания зависимости. Инъекция зависимостей - это конструктор, сеттер и интерфейс. Ядро IOC состоит в том, чтобы не зависеть от конкретных классов, а определять абстрактные методы (скажем, интерфейс / абстрактный класс) и использовать этот абстрактный метод вызова конкретного класса. Подобно Factory pattern возвращают базовый класс или интерфейс. Подобная инъекция зависимости использует базовый класс / интерфейс для установки значения для объектов.


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


При инъекции зависимостей клиенту не нужно самостоятельно получать свои зависимости, а все готово заранее.

С заводами кто-то должен их вызвать, чтобы получить сгенерированные объекты до того места, где они необходимы.

Разница заключается в основном в этой одной строке, где выполняется вызов фабрики и выборка построенного объекта.

Но с заводами вы должны написать эту 1 строку везде, где вам нужен такой объект. С DI вам просто нужно создать проводку (связь между использованием и созданным объектом) один раз и просто опираться на присутствие объекта после этого повсюду. С другой стороны, DI часто требует немного больше (сколько зависит от структуры) работы на стороне подготовки.


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

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

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

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

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


С фабрикой вы можете группировать связанные интерфейсы, поэтому, если переданные параметры могут быть сгруппированы на фабрике, то это также хорошее решение для constructor overinjection посмотрите на этот код *):

public AddressModelFactory(IAddressAttributeService addressAttributeService,
        IAddressAttributeParser addressAttributeParser,
        ILocalizationService localizationService,
        IStateProvinceService stateProvinceService,
        IAddressAttributeFormatter addressAttributeFormatter)
    {
        this._addressAttributeService = addressAttributeService;
        this._addressAttributeParser = addressAttributeParser;
        this._localizationService = localizationService;
        this._stateProvinceService = stateProvinceService;
        this._addressAttributeFormatter = addressAttributeFormatter;
    }

Посмотрите на конструктор, вам нужно только передать IAddressModelFactory , так что меньше параметров *):

 public CustomerController(IAddressModelFactory addressModelFactory,
        ICustomerModelFactory customerModelFactory,
        IAuthenticationService authenticationService,
        DateTimeSettings dateTimeSettings,
        TaxSettings taxSettings,
        ILocalizationService localizationService,
        IWorkContext workContext,
        IStoreContext storeContext,
        ICustomerService customerService,
        ICustomerAttributeParser customerAttributeParser,
        ICustomerAttributeService customerAttributeService,
        IGenericAttributeService genericAttributeService,
        ICustomerRegistrationService customerRegistrationService,
        ITaxService taxService,
        CustomerSettings customerSettings,
        AddressSettings addressSettings,...

Вы видите CustomerControllerмного переданных параметров. Да, вы можете видеть это, constructor overinjectionно это так, как работает DI. И нет ничего плохого в этом CustomerController.

*) Код из nopCommerce.


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


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

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

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

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

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

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

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

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

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

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

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

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

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"); 

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

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

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

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


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

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

Проверьте эту ссылку: Инъекция зависимостей


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

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

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


Говоря простыми словами, метод Injection vs Factory подразумевает механизм push vs pull соответственно.

С механизмом pull: класс косвенно зависит от метода Factory, который, в свою очередь, зависит от конкретных классов.

Механизм Push: компонент корня может быть сконфигурирован со всеми зависимыми компонентами в одном месте и, таким образом, способствует высокому обслуживанию и ослаблению сцепления.

С ответственностью Factory method по-прежнему лежит класс (хотя и косвенно) для создания нового объекта, где, как и в случае инъекции зависимостей, ответственность за аутсорсинг (за счет утечки абстракции)


Заводская модель

Модель проектирования завода характеризуется

  • Интерфейс
  • Классы реализации
  • Завод

Вы можете наблюдать несколько вещей, когда задаетесь вопросом, как ниже

  • Когда фабрика создаст объект для классов реализации - время выполнения или время компиляции?
  • Что делать, если вы хотите переключить реализацию во время выполнения? - Невозможно

Они обрабатываются инъекцией зависимостей.

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

У вас могут быть разные способы, с помощью которых вы можете вводить зависимость. Для простоты можно перейти с интерфейсом Injection

В DI контейнер создает необходимые экземпляры и «внедряет» их в объект.

Таким образом, исключается статический экземпляр.

Пример:

public class MyClass{

  MyInterface find= null;

  //Constructor- During the object instantiation

  public MyClass(MyInterface myInterface ) {

       find = myInterface ;
  }

  public void myMethod(){

       find.doSomething();

  }
}

По-моему, использование инъекции зависимостей намного лучше, если вы: 1. развертывание своего кода в небольшом разделе, потому что он отлично справляется с развязкой одного большого кода. 2. testability является одним из случаев, когда DI одобрен для использования, потому что вы можете легко издеваться над не разделенными объектами. с помощью интерфейсов вы можете легко издеваться и тестировать каждый объект. 3. вы можете одновременно пересматривать каждую часть программы без необходимости кодировать другую ее часть, так как она свободно развязана.


Я думаю, что они ортогональны и могут использоваться вместе. Позвольте мне показать вам пример, который я недавно встретил на работе:

Мы использовали платформу Spring в Java для DI. Одноэлементный класс ( Parent) должен был создавать новые объекты другого класса ( Child), и у них были сложные соавторы:

@Component
class Parent {
    // ...
    @Autowired
    Parent(Dep1 dep1, Dep2 dep2, ..., DepN depN) {
        this.dep1 = dep1;
        this.dep2 = dep2;
    }

    void method(int p) {
        Child c = new Child(dep1, dep2, ..., depN, p);
        // ...
    }
}

В этом примере Parentнужно принимать DepXэкземпляры только для передачи их Childконструктору. Проблемы с этим:

  1. Parentимеет больше знаний, Childчем должно
  2. Parent имеет больше сотрудников, чем должно
  3. Добавление зависимостей Childсвязано с изменениемParent

Это когда я понял, Factoryчто здесь идеально подойдет:

  1. Он скрывает все, кроме истинных параметров Childкласса, как видно изParent
  2. Он инкапсулирует знание создания a Child, которое может быть централизовано в конфигурации DI.

Это упрощенный Parentкласс и ChildFactoryкласс:

@Component
class Parent {
    // ...
    @Autowired
    Parent(ChildFactory childFactory) {
        this.childFactory = childFactory;
    }

    void method(int p) {
        Child c = childFactory.newChild(p);
        // ...
    }
}

@Component
class ChildFactory {
    // ...
    @Autowired
    Parent(Dep1 dep1, Dep2 dep2, ..., DepN depN) {
        this.dep1 = dep1;
        this.dep2 = dep2;
        // ...
        this.depN = depN;
    }

    Child newChild(int p) {
        return new Child(dep1, dep2, ..., depN, p);
    }
}




design-patterns