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

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

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

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

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

Ответы [ 27 ]

1 голос
/ 28 октября 2009

Мои мысли:

Внедрение зависимости: передать соавторы в качестве параметров конструкторам. Dependency Injection Framework: универсальная и настраиваемая фабрика для создания объектов, передаваемых в качестве параметров конструкторам.

1 голос
/ 21 ноября 2017

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

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

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

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

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

1 голос
/ 28 ноября 2017

С фабрикой вы можете группировать связанные интерфейсы, поэтому, если переданные параметры могут быть сгруппированы на фабрике, тогда это также хорошее решение для 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.

0 голосов
/ 13 февраля 2018

Проще говоря, Внедрение зависимостей по сравнению с заводским методом подразумевает механизм «толчок против тяги» соответственно.

Механизм извлечения : класс косвенно зависит от метода фабрики, который, в свою очередь, зависит от конкретных классов.

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

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

0 голосов
/ 16 апреля 2018

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

Мы использовали среду Spring в Java для DI. Класс Singleton (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. Он заключает в себе знания о создании 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);
    }
}
0 голосов
/ 25 июня 2018

Я использую и , чтобы создать стратегию Inversion Of Control с большей читабельностью для разработчиков, которым необходимо поддерживать ее после меня.

Я использую Фабрику для создания различных объектов Layer (Бизнес, Доступ к данным).

ICarBusiness carBusiness = BusinessFactory.CreateCarBusiness();

Другой разработчик увидит это и при создании объекта Business Layer он смотрит в BusinessFactory, и Intellisense предоставляет разработчику все возможные бизнес-уровни для создания. Не нужно играть в игру, найдите интерфейс, который я хочу создать.

Эта структура уже является инверсией контроля. Я больше не несу ответственности за создание конкретного объекта. Но вам все равно нужно обеспечить внедрение зависимостей, чтобы можно было легко что-то менять. Создание своего собственного Dependency Injection нелепо, поэтому я использую Unity. В CreateCarBusiness () я прошу Unity to Resolve, какой класс принадлежит этому и его жизни.

Таким образом, мой код Factory Inpendency Injection имеет следующую структуру:

public static class BusinessFactory
{
    public static ICarBusiness CreateCarBusiness()
    {
       return Container.Resolve<ICarBusiness>();
    }
}

Теперь у меня есть преимущество обоих. Мой код также более удобен для чтения другими разработчиками по сравнению с областями моих объектов, которые я использую, вместо Constructor Dependency Injection, которое просто говорит, что каждый объект доступен при создании класса.

Я использую это, чтобы изменить свой доступ к данным базы данных на пользовательский кодированный слой доступа к данным при создании модульных тестов. Я не хочу, чтобы мои модульные тесты связывались с базами данных, веб-серверами, серверами электронной почты и т. Д. Им нужно тестировать мой бизнес-уровень, потому что именно там находится интеллект.

0 голосов
/ 22 декабря 2014

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

...