Из Википедии, бесплатной энциклопедии
  (Перенаправлено из внедрения зависимостей )
Перейти к навигации Перейти к поиску

В разработке программного обеспечения , внедрение зависимостей представляет собой метод , в котором объект принимает другие объекты , которые она зависит. Эти другие объекты называются зависимостями. В типичных отношениях «использование» принимающий объект называется клиентом, а переданный (то есть «внедренный») объект - службой . Код, который передает услугу клиенту, может быть разнообразным и называется инжектором. Вместо того, чтобы клиент указывал, какую службу он будет использовать, инжектор сообщает клиенту, какую службу использовать. «Внедрение» относится к передаче зависимости (службы) объекту (клиенту), который будет ее использовать.

Служба выполнена часть клиента государства . [1] Передача службы клиенту вместо того, чтобы позволить клиенту создать или найти службу , является фундаментальным требованием шаблона.

Целью внедрения зависимостей является разделение задач построения и использования объектов. Это может повысить удобочитаемость и повторное использование кода.

Внедрение зависимостей - одна из форм более широкой техники инверсии управления . Клиент, который хочет вызвать некоторые службы, не должен знать, как создавать эти службы. Вместо этого клиент делегирует ответственность за предоставление своих услуг внешнему коду (инжектору). Клиент не имеет права вызывать код инжектора; [2]это инжектор, который создает услуги. Затем инжектор вводит (передает) услуги клиенту, которые могут уже существовать или могут быть созданы инжектором. Затем клиент пользуется услугами. Это означает, что клиенту не нужно знать о инжекторе, о том, как создавать сервисы, или даже о том, какие фактические сервисы он использует. Клиенту нужно знать только о внутренних интерфейсах сервисов, потому что они определяют, как клиент может использовать сервисы. Это отделяет ответственность за «использование» от ответственности за «строительство».

Использует [ редактировать ]

Внедрение зависимостей решает такие проблемы, как: [3]

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

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

С внедрением зависимостей класс больше не отвечает за создание экземпляров требуемых объектов. Эти обязанности передаются независимым службам.

Обзор [ править ]

Инъекция зависимости для пятилетних детей

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

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

Джон Манч, 28 октября 2009 г. [4] [5] [6]

Внедрение зависимостей отделяет создание зависимостей клиента от поведения клиента, что позволяет слабо связывать проекты программ [7] и следовать принципам инверсии зависимостей и единой ответственности . [4] [8] Он прямо контрастирует с шаблоном локатора сервисов , который позволяет клиентам узнать о системе, которую они используют для поиска зависимостей.

Внедрение, основная единица внедрения зависимостей, не является новым или настраиваемым механизмом. Он работает так же, как « передача параметров ». [9] Ссылаясь на «передачу параметров» как на инъекцию, подразумевается, что это делается для того, чтобы изолировать клиента от деталей.

Инъекция также касается того, что контролирует передачу (но не клиента), и не зависит от того, как выполняется передача, будь то передача ссылки или значения.

Внедрение зависимостей включает четыре роли:

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

По аналогии

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

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

Эти интерфейсы являются типами клиент ожидает , что его зависимостей быть. Проблема в том, что они делают доступным. Они действительно могут быть типами интерфейсов, реализованными службами, но также могут быть абстрактными классами или даже самими конкретными службами, хотя последнее нарушит DIP [10] и принесет в жертву динамическую развязку, которая позволяет проводить тестирование. Требуется только, чтобы клиент не знал, что они собой представляют, и поэтому никогда не относился к ним как к конкретным, например, путем их конструирования или расширения.

У клиента не должно быть конкретных знаний о конкретной реализации своих зависимостей. Он должен знать только имя интерфейса и API . В результате клиенту не нужно будет ничего менять, даже если изменится то, что стоит за интерфейсом. Однако, если интерфейс преобразован из класса в тип интерфейса (или наоборот), клиент должен быть перекомпилирован. [11] Это важно, если клиент и услуги публикуются отдельно. Это неудачное соединение не может разрешить внедрение зависимостей.

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

Внедрение зависимостей может применяться как дисциплина, требующая, чтобы все объекты разделяли конструкцию и поведение. Использование структуры DI для выполнения построения может привести к запрету использования ключевого слова new или, что менее строго, разрешению только прямого построения объектов значений . [12] [13] [14] [15]

Таксономия [ править ]

Инверсия управления (IoC) является более общей, чем внедрение зависимостей. Проще говоря, IoC означает позволить другому коду позвонить вам, а не настаивать на выполнении вызова. Примером IoC без внедрения зависимостей является шаблон метода шаблона . Здесь полиморфизм достигается за счет создания подклассов , то есть наследования . [16]

Внедрение зависимостей реализует IoC через композицию, поэтому часто идентично шаблону стратегии , но хотя шаблон стратегии предназначен для взаимозаменяемости зависимостей на протяжении всего жизненного цикла объекта , при внедрении зависимостей может использоваться только один экземпляр зависимости. . [17] Это все еще достигает полиморфизма, но через делегирование и композицию .

Фреймворки внедрения зависимостей [ править ]

Инфраструктуры приложений, такие как CDI и его реализация Weld , Spring , Guice , Play framework , Salta , Glassfish HK2 , Dagger и Managed Extensibility Framework (MEF), поддерживают внедрение зависимостей, но не требуют внедрения зависимостей. [18] [19]

Преимущества [ править ]

  • Внедрение зависимостей позволяет клиенту гибко настраиваться. Фиксируется только поведение клиента. Клиент может воздействовать на все, что поддерживает внутренний интерфейс, которого ожидает клиент. [20]
  • Внедрение зависимостей может использоваться для внесения деталей конфигурации системы в файлы конфигурации, что позволяет реконфигурировать систему без перекомпиляции. Отдельные конфигурации могут быть написаны для разных ситуаций, требующих разных реализаций компонентов. Это включает, но не ограничивается, тестирование. [21]
  • Поскольку внедрение зависимостей не требует каких-либо изменений в поведении кода, его можно применить к унаследованному коду в качестве рефакторинга . В результате клиенты становятся более независимыми и которые легче тестировать изолированно, используя заглушки или имитирующие объекты, которые имитируют другие объекты, не тестируемые. Такая простота тестирования часто является первым преимуществом, которое замечают при использовании внедрения зависимостей. [22]
  • Внедрение зависимостей позволяет клиенту удалить все знания о конкретной реализации, которые ему необходимы. Это помогает изолировать клиента от влияния изменений конструкции и дефектов. Это способствует повторному использованию, тестируемости и ремонтопригодности. [23]
  • Сокращение шаблонного кода в объектах приложения, поскольку вся работа по инициализации или настройке зависимостей выполняется компонентом поставщика. [23]
  • Внедрение зависимостей допускает параллельную или независимую разработку. Два разработчика могут независимо разрабатывать классы, которые используют друг друга, при этом им нужно знать только интерфейс, через который классы будут обмениваться данными. Плагины часто разрабатываются сторонними магазинами, которые даже не разговаривают с разработчиками, создавшими продукт, использующий плагины. [24]
  • Внедрение зависимостей уменьшает связь между классом и его зависимостью. [25] [26]

Недостатки [ править ]

  • Внедрение зависимостей создает клиентов, которым требуются детали конфигурации, предоставляемые строительным кодом. Это может быть обременительным, когда доступны очевидные значения по умолчанию. [24]
  • Внедрение зависимостей может затруднить отслеживание (чтение) кода, поскольку оно отделяет поведение от построения. Это означает, что разработчики должны обращаться к большему количеству файлов, чтобы следить за работой системы. [24]
  • Фреймворки внедрения зависимостей реализованы с помощью отражения или динамического программирования. Это может затруднить использование таких средств автоматизации IDE, как «поиск ссылок», «отображение иерархии вызовов» и безопасный рефакторинг. [27]
  • Внедрение зависимостей обычно требует дополнительных усилий на начальном этапе разработки, поскольку нельзя вызвать что-то нужное, когда и где это необходимо, но нужно попросить, чтобы это было внедрено, а затем убедиться, что оно было внедрено. [28]
  • Внедрение зависимостей заставляет сложность перемещаться из классов в связи между классами, что не всегда может быть желательным или легко управляемым. [29]
  • Внедрение зависимостей может стимулировать зависимость от инфраструктуры внедрения зависимостей. [29] [30] [31]

Структура [ править ]

Схема классов и последовательности UML [ править ]

Пример класса UML и диаграммы последовательности для шаблона проектирования внедрения зависимостей. [32]

В приведенной выше UML диаграммы классов , то Clientкласс , который требует ServiceAи ServiceBобъектов не создавать экземпляры ServiceA1и ServiceB1классов непосредственно. Вместо этого Injectorкласс создает объекты и внедряет их в Client, что делает Clientнезависимым от того, как создаются объекты (какие конкретные классы создаются). UML - диаграмма последовательности показывает время выполнения взаимодействий: объект создает и объекты. После этого создает объект и впрыскивает и объекты.
InjectorServiceA1ServiceB1InjectorClientServiceA1ServiceB1

Примеры [ править ]

Без внедрения зависимостей [ править ]

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

// Пример без внедрения зависимости public  class  Client  {  // Внутренняя ссылка на службу, используемую этим клиентом  private  ExampleService  service ; // Конструктор  Client ()  {  // Укажите конкретную реализацию в конструкторе вместо использования внедрения зависимостей  service  =  new  ExampleService ();  } // Метод в этом клиенте, который использует службы  public  String  greet ()  {  return  "Hello"  +  service . getName ();  } }

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

Типы внедрения зависимостей [ править ]

Есть как минимум три способа, которыми клиентский объект может получить ссылку на внешний модуль: [33]

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

Другие типы [ править ]

Фреймворки DI могут иметь другие типы инъекций помимо представленных выше. [34]

Фреймворки тестирования также могут использовать другие типы. Некоторые современные среды тестирования даже не требуют, чтобы клиенты активно принимали внедрение зависимостей, что делает устаревший код пригодным для тестирования. В частности, в языке Java можно использовать отражение, чтобы сделать частные атрибуты общедоступными при тестировании и, таким образом, принимать инъекции путем присваивания. [35]

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

Внедрение конструктора [ править ]

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

// Конструктор- клиент ( сервис-  сервис )  {  // Сохраняем ссылку на переданный сервис внутри этого клиента  this . service  =  служба ; }

Сеттер инъекций [ править ]

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

// Метод установки public  void  setService ( Service  service )  {  // Сохраняем ссылку на переданную службу внутри этого клиента.  это . service  =  служба ; }

Внедрение интерфейса [ править ]

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

// Интерфейс установщика сервисов. общедоступный  интерфейс  ServiceSetter  {  общедоступный  недействительный  setService ( сервисная  служба ); }// Класс клиента Открытый  класс  Client  реализует  ServiceSetter  {  // Внутренняя ссылка на службу, используемую этим клиентом.  частное  сервисное  обслуживание ; // Устанавливаем службу, которую должен использовать этот клиент.  @Override  public  void  setService ( Сервисная  служба )  {  this . service  =  служба ;  } }

Сравнение внедрения конструктора [ править ]

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

// Конструктор- клиент ( служба  службы ,  служба  otherService )  {  if  ( service  ==  null )  {  выбросить  новое  исключение InvalidParameterException ( "служба не должна быть нулевой" );  }  if  ( otherService  ==  null )  {  throw  new  InvalidParameterException ( «otherService не должен быть нулевым» );  } // Сохраняем ссылки на сервисы внутри этого клиента  this . service  =  служба ;  это . otherService  =  otherService ; }

Сравнение инъекций сеттера [ править ]

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

// Устанавливаем службу, которая будет использоваться этим клиентом public  void  setService ( Service  service )  {  if  ( service  ==  null )  {  throw  new  InvalidParameterException ( "service не должно быть нулевым" );  }  это . service  =  служба ; }// Устанавливаем другую службу для использования этим клиентом public  void  setOtherService ( Service  otherService )  {  if  ( otherService  ==  null )  {  throw  new  InvalidParameterException ( "otherService не должен быть нулевым" );  }  это . otherService  =  otherService ; }

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

// Устанавливаем службу, которая будет использоваться этим клиентом public  void  setService ( Service  service )  {  this . service  =  служба ; }// Устанавливаем другую службу, которая будет использоваться этим клиентом public  void  setOtherService ( Service  otherService )  {  this . otherService  =  otherService ; }// Проверяем ссылки на службы этого клиента private  void  validateState ()  {  if  ( service  ==  null )  {  throw  new  IllegalStateException ( "service не должно быть нулевым" );  }  if  ( otherService  ==  null )  {  выбросить  новое  исключение IllegalStateException ( «otherService не должен быть нулевым» );  } }// Метод, использующий эту службу, ссылается на public  void  doSomething ()  {  validateState ();  сервис . doYourThing ();  otherService . doYourThing (); }

Сравнение внедрения интерфейса [ править ]

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

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

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

// Интерфейс установщика сервисов. общедоступный  интерфейс  ServiceSetter  {  общедоступный  недействительный  setService ( сервисная  служба ); }// Класс клиента Открытый  класс  Client  реализует  ServiceSetter  {  // Внутренняя ссылка на службу, используемую этим клиентом.  частное  сервисное  обслуживание ; // Устанавливаем службу, которую должен использовать этот клиент.  @Override  public  void  setService ( Сервисная  служба )  {  this . service  =  служба ;  } }// Класс инжектора открытый  класс  ServiceInjector  { Set < ServiceSetter >  clients ; public  void  inject ( клиент ServiceSetter  ) { клиенты . добавить ( клиент ); клиент . setService ( новый ServiceFoo ()); } public void switchToBar () { для ( Клиент- клиент : клиенты ) { клиент .          setService ( новый  ServiceBar ()); } } }// Сервисные классы открытый  класс  ServiceFoo  реализует  Service  {} открытый  класс  ServiceBar  реализует  Service  {}

Примеры сборки [ править ]

Ручная сборка в основном вручную - это один из способов реализации внедрения зависимостей.

public  class  Injector  {  public  static  void  main ( String []  args )  {  // Сначала строим зависимости  Service  service  =  new  ExampleService (); // Внедрение службы в стиле конструктора  Client  client  =  new  Client ( service ); // Используем объекты  System . из . println ( клиент . приветствие ());  } }

В приведенном выше примере граф объекта создается вручную, а затем он вызывается в какой-то момент, чтобы запустить его работу. Важно отметить, что этот инжектор не чистый. Он использует один из создаваемых им объектов. Он имеет чисто конструктивные отношения с ExampleService, но смешивает построение и использование Client. Это не должно быть обычным явлением. Однако это неизбежно. Точно так же, как объектно-ориентированному программному обеспечению для начала нужен не объектно-ориентированный статический метод, такой как main (), графу внедренных зависимостей требуется по крайней мере одна (предпочтительно только одна) точка входа, чтобы все это начало.

Ручное построение в основном методе может быть непростым и может включать вызов строителей , фабрик или других строительных шаблонов . Это может быть довольно сложно и абстрактно. Граница переходит от ручного внедрения зависимостей к внедрению зависимостей фреймворка, как только код построения больше не является индивидуальным для приложения и вместо этого становится универсальным. [36]

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

import  org.springframework.beans.factory.BeanFactory ; import  org.springframework.context.ApplicationContext ; import  org.springframework.context.support.ClassPathXmlApplicationContext ;public  class  Injector  { public  static  void  main ( String []  args )  { // - Сборка объектов - // BeanFactory  beanfactory  =  new  ClassPathXmlApplicationContext ( "Beans.xml" ); Клиент  client  =  ( Клиент )  beanfactory . getBean ( "клиент" );// - Использование объектов - // System . из . println ( клиент . приветствие ()); } }

Такие фреймворки, как Spring, позволяют выводить детали сборки в файлы конфигурации. Этот код (вверху) конструирует объекты и связывает их вместе в соответствии с Beans.xml (внизу). ExampleService все еще создается, хотя он упоминается только ниже. Таким образом можно определить длинный и сложный граф объектов, и единственный класс, упомянутый в коде, будет иметь метод точки входа, которым в данном случае является greet ().

 <? xml version = "1.0" encoding = "UTF-8"?>  <beans  xmlns = "http://www.springframework.org/schema/beans"  xmlns: xsi = "http://www.w3.org / 2001 / XMLSchema-instance "  xsi: schemaLocation = " http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd " > <bean  id = "service"  class = "ExampleService" >  </bean> <bean  id = "client"  class = "Client" >  <constructor-arg  value = "service"  />  </bean> </beans>

В приведенном выше примере Клиент и Служба не должны подвергаться каким-либо изменениям, которые должны быть предоставлены Spring. Им разрешено оставаться простыми POJO . [37] [38] [39] Это показывает, как Spring может соединять сервисы и клиентов, которые полностью игнорируют его существование. Этого нельзя было бы сказать, если бы к классам были добавлены аннотации Spring. Сохраняя характерные для Spring аннотации и вызовы от распространения среди многих классов, система остается лишь слабо зависимой от Spring. [30] Это может быть важно, если система намеревается пережить Spring.

Решение о сохранении чистоты POJO не обходится без затрат. Вместо того, чтобы тратить усилия на разработку и обслуживание сложных файлов конфигурации, можно просто использовать аннотации для пометки классов и позволить Spring сделать остальную работу. Устранение зависимостей может быть простым, если они следуют соглашению, например, сопоставлению по типу или по имени. Это выбор соглашения, а не конфигурации . [40] Также можно утверждать, что при рефакторинге в другую структуру удаление аннотаций, специфичных для данной среды, было бы тривиальной частью задачи [41], и многие аннотации внедрения теперь стандартизированы. [42] [43]

import  org.springframework.beans.factory.BeanFactory ; import  org.springframework.context.ApplicationContext ; import  org.springframework.context.annotation.AnnotationConfigApplicationContext ;public  class  Injector  { public  static  void  main ( String []  args )  { // Собираем  объекты BeanFactory beanfactory  =  new  AnnotationConfigApplicationContext ( MyConfiguration . class ); Клиент  client  =  beanfactory . getBean ( Клиент . класс );// Используем объекты System . из . println ( клиент . приветствие ()); } }
import  org.springframework.context.annotation.Bean ; import  org.springframework.context.annotation.ComponentScan ; import  org.springframework.context.annotation.Configuration ;@ComponentScan публичный  класс  MyConfiguration  {  @Bean  публичный  клиентский  клиент ( сервис ExampleService  ) { return new Client ( service ); } }     
@Component общедоступный  класс  ExampleService  {  public  String  getName ()  {  return  "World!" ;  } }

Сравнение сборок [ править ]

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

Убрав все знания об инъекторе, чистый клиент, свободный от знаний о внешнем мире, останется позади. Однако любой объект, использующий другие объекты, может считаться клиентом. Объект, содержащий main, не исключение. Этот основной объект не использует внедрение зависимостей. Фактически он использует шаблон локатора сервисов. Этого нельзя избежать, потому что выбор реализаций сервиса нужно где-то делать.

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

Шаблон внедрения зависимости [ править ]

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

public  static  void  main ( String []  args )  выбрасывает  IOException  { // Строительный код.  Greeter  greeter  =  новый  Greeter ( System . Out );  // Это может быть много линий, которые соединяют множество объектов  // Код поведения.  встречающий . приветствовать ();  // Это один вызов одного метода для одного объекта в графе объектов }class  Greeter  {  public  void  greet ()  {  this . из . println ( «Привет, мир!» );  }  public  Greeter ( PrintStream  out )  {  this . out  =  out ;  }  частный  PrintStream  out ; }

Пример AngularJS [ править ]

В структуре AngularJS есть только три способа, которыми компонент (объект или функция) может напрямую обращаться к своим зависимостям:

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

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

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

функция  SomeClass ( приветствующий )  {  this . greeter  =  встречающий ; }SomeClass . прототип . doSomething  =  функция ( имя )  {  это . встречающий . приветствовать ( имя ); }

В приведенном выше примере SomeClassне имеет отношения к созданию или обнаружению зависимости средства приветствия, он просто передается средству приветствия при его создании.

Это желательно, но возлагает ответственность за зависимость от кода, который конструирует SomeClass.

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

Вот пример использования сервиса инжектора:

// Предоставляем информацию о подключении в модуле var  myModule  =  angular . модуль ( 'myModule' ,  []);// Обучаем инжектора, как создать службу приветствия. // программа приветствия зависит от службы $ window. // Служба приветствия - это объект, // содержащий метод приветствия. myModule . factory ( 'приветствие' ,  функция ( $ window )  {  return  {  greet :  function ( text )  {  $ window . alert ( text );  }  }; });

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

вар  инжектор  =  угловой . инжектор ([ 'myModule' ,  'ng' ]); var  greeter  =  инжектор . получить ( 'приветствующий' );

Запрос зависимостей решает проблему жесткого кодирования, но также означает, что инжектор необходимо передавать по всему приложению. Прохождение инжектора нарушает закон Деметры . Чтобы исправить это, мы используем декларативную нотацию в наших HTML-шаблонах, чтобы передать ответственность за создание компонентов инжектору, как в этом примере:

< div  ng-controller = "MyController" >  < button  ng-click = "sayHello ()" > Привет </ button > </ div >
функция  MyController ( $ scope ,  greeter )  {  $ scope . sayHello  =  function ()  {  приветствующий . приветствовать ( 'Привет, мир' );  }; }

Когда AngularJS компилирует HTML, он обрабатывает ng-controllerдирективу, которая, в свою очередь, просит инжектор создать экземпляр контроллера и его зависимости.

injector.instantiate (MyController);

Все это делается за кадром. Поскольку ng-controllerсоздание экземпляра класса передается инжектору, он может удовлетворить все зависимости, MyControllerдаже если контроллер никогда не узнает о инжекторе. Код приложения просто объявляет необходимые ему зависимости без необходимости иметь дело с инжектором. Эта установка не нарушает Закон Деметры .

C # [ править ]

Пример инъекции конструктора , сеттер инъекции и инъекции интерфейса на C #

используя  Систему ;Пространство имена  внедрение зависимости {  // интерфейс для библиотеки  интерфейса  IGamepadFunctionality  {  Строки  GetGamepadName ();  void  SetVibrationPower ( float  InPower );  } // Конкретная реализация функциональности контроллера xbox  class  XBoxGamepad  :  IGamepadFunctionality  {  readonly  String  GamepadName  =  "XBox Controller" ;  float  VibrationPower  =  1.0f ;  общедоступная  строка  GetGamepadName ()  =>  GamepadName ;  public  void  SetVibrationPower ( float  InPower )  =>  VibrationPower  =  Math . Зажим ( InPower ,  0,0f ,  1,0f); } // Конкретная реализация  класса  функциональности контроллера playstation PlaystationJoystick  :  IGamepadFunctionality  {  readonly  String  ControllerName  =  "Playstation controller" ;  плавать  VibratingPower  =  100.0f ;  общедоступная  строка  GetGamepadName ()  =>  ControllerName ;  общественного  недействительный  SetVibrationPower ( поплавок  InPower )  =>  VibratingPower  =  Math . Зажим ( InPower  *  100.0f,  0,0f ,  100,0f );  } // Конкретная реализация функциональности парового контроллера  class  SteamController  :  IGamepadFunctionality  {  readonly  String  JoystickName  =  "Steam controller" ;  двойная  вибрация  =  1.0 ;  общедоступная  строка  GetGamepadName ()  =>  JoystickName ;  public  void  SetVibrationPower ( float  InPower )  =>  Вибрация  =  Конвертировать . ToDouble ( Math . Clamp (InPower ,  0.0f ,  1.0f ));  } // Интерфейс для внедрения функциональности геймпада  interface  IGamepadFunctionalityInjector  {  void  InjectFunctionality ( IGamepadFunctionality  InGamepadFunctionality );  } class  CGamepad  :  IGamepadFunctionalityInjector  {  IGamepadFunctionality  _GamepadFunctionality ; public  CGamepad ()  { }  // Внедрение конструктора  public  CGamepad ( IGamepadFunctionality  InGamepadFunctionality )  =>  _GamepadFunctionality  =  InGamepadFunctionality ; //  Установление инъекции public  void  SetGamepadFunctionality ( IGamepadFunctionality  InGamepadFunctionality )  =>  _GamepadFunctionality  =  InGamepadFunctionality ; // Внедрение интерфейса  public  void  InjectFunctionality ( IGamepadFunctionality  InGamepadFunctionality )  =>  _GamepadFunctionality  =  InGamepadFunctionality ; public  void  Showcase ()  {  String  Message  =  String . Format ( «Сейчас мы используем {0}, вы хотите изменить мощность вибрации? \ R \ n» ,  _GamepadFunctionality . GetGamepadName ());  Консоль . WriteLine ( Сообщение );  }  } enum  EPlatforms :  byte  {  Xbox ,  Playstation ,  Steam  } класс  CGameEngine  {  EPlatforms  _Platform ;  CGamepad  _Gamepad ;  public  void  SetPlatform ( EPlatforms  InPlatform )  {  _Platform  =  InPlatform ;  switch ( _Platform )  {  case  EPlatforms . Xbox : // внедряет зависимость от класса XBoxGamepad через Внедрение конструктора  _Gamepad  =  new  CGamepad ( new  XBoxGamepad ());  перерыв ;  case  EPlatforms . Playstation :  _Gamepad  =  новый  CGamepad (); // внедряет зависимость от класса PlaystationJoystick через Setter Injection  _Gamepad . SetGamepadFunctionality ( новый  PlaystationJoystick ());  перерыв ;  case  EPlatforms . Steam :  _Gamepad  =  новый  CGamepad (); // внедряет зависимость от класса SteamController через  Внедрение интерфейса _Gamepad . InjectFunctionality ( новый  SteamController ());  перерыв ;  } _Геймпад . Витрина ();  }  }  класс  Program  {  static  void  Main ( string []  args )  {  Консоль . WriteLine ( «Привет, мир!» );  CGameEngine  Engine  =  новый  CGameEngine (); Двигатель . SetPlatform ( EPlatforms . Steam ); Двигатель . SetPlatform ( EPlatforms . Xbox ); Двигатель . SetPlatform ( EPlatforms . Playstation );  }  } }

См. Также [ править ]

  • Язык описания архитектуры
  • Заводской образец
  • Инверсия контроля
  • Плагин (вычисления)
  • Шаблон стратегии
  • AngularJS
  • Шаблон локатора услуг
  • Параметр (компьютерное программирование)
  • Quaject

Ссылки [ править ]

  1. ^ IT, Титан. "Джеймс Шор: Демистификация инъекции зависимости" . www.jamesshore.com . Проверено 18 июля 2015 .
  2. ^ "Голливудский принцип" . c2.com . Проверено 19 июля 2015 .
  3. ^ «Шаблон проектирования внедрения зависимостей - проблема, решение и применимость» . w3sDesign.com . Проверено 12 августа 2017 .
  4. ^ a b Симан, Марк (октябрь 2011 г.). Внедрение зависимостей в .NET . Публикации Мэннинга. п. 4. ISBN 9781935182504.
  5. ^ «Внедрение зависимостей в NET» (PDF) . philkildea.co.uk . п. 4 . Проверено 18 июля 2015 .
  6. ^ "Как объяснить инъекцию зависимости 5-летнему ребенку?" . stackoverflow.com . Проверено 18 июля 2015 .
  7. ^ Seemann, Марк. «Внедрение зависимостей - слабая связь» . blog.ploeh.dk . Проверено 28 июля 2015 .
  8. ^ Нико Шварц, Мирча Лунгу, Оскар Нирстраз, «Сьюз: разделение обязанностей и статических методов для точной настройки», Журнал объектных технологий, Том 11, № 1 (апрель 2012 г.), стр. 3: 1-23
  9. ^ «Передача информации методу или конструктору (Учебники Java ™> Изучение языка Java> Классы и объекты)» . docs.oracle.com . Проверено 18 июля 2015 .
  10. ^ «Карри принципа инверсии зависимостей (DIP), инверсии управления (IoC), внедрения зависимостей (DI) и контейнера IoC - CodeProject» . www.codeproject.com . Проверено 8 августа 2015 .
  11. ^ «Как заставить» программу работать с интерфейсом «без использования java-интерфейса в java 1.6» . programmers.stackexchange.com . Проверено 19 июля 2015 .
  12. ^ «К« новому »или не к« новому »…» . Проверено 18 июля 2015 .
  13. ^ «Как написать тестируемый код» . www.loosecouplings.com . Проверено 18 июля 2015 .
  14. ^ «Написание чистого, проверяемого кода» . www.ethanresnick.com . Проверено 18 июля 2015 .
  15. ^ Сирони, Джорджио. «Когда делать инъекции: различие между новыми и инъекционными препаратами - невидимо для глаз» . www.giorgiosironi.com . Проверено 18 июля 2015 .
  16. ^ «Инверсия управления против внедрения зависимостей» . stackoverflow.com . Проверено 5 августа 2015 .
  17. ^ "В чем разница между шаблоном стратегии и внедрением зависимостей?" . stackoverflow.com . Проверено 18 июля 2015 .
  18. ^ "Внедрение зависимостей! = С использованием контейнера DI" . www.loosecouplings.com . Проверено 18 июля 2015 .
  19. ^ «Черная овца» DIY-DI »Печать» . blacksheep.parry.org . Архивировано из оригинала на 2015-06-27 . Проверено 18 июля 2015 .
  20. ^ https://python.astrotech.io/design-patterns/structural/dependency-injection.html
  21. ^ http://python-dependency-injector.ets-labs.org/introduction/di_in_python.html
  22. ^ https://visualstudiomagazine.com/articles/2014/07/01/larger-applications.aspx
  23. ^ a b «Программа Java Community Process (SM) - JSR: запросы спецификации Java - деталь JSR № 330» . jcp.org . Проверено 18 июля 2015 .
  24. ^ а б в https://dzone.com/articles/how-dependency-injection-di-works-in-spring-java-a
  25. ^ "Городской канук, а: О внедрении зависимостей и нарушениях инкапсуляции" . www.bryancook.net . Проверено 18 июля 2015 .
  26. ^ «Шаблон проектирования внедрения зависимостей» . msdn.microsoft.com . Проверено 18 июля 2015 .
  27. ^ https://www.freecodecamp.org/news/a-quick-intro-to-dependency-injection-what-it-is-and-when-to-use-it-7578c84fa88f/
  28. ^ https://www.professionalqa.com/dependency-injection
  29. ^ a b "Каковы недостатки использования внедрения зависимостей?" . stackoverflow.com . Проверено 18 июля 2015 .
  30. ^ a b «Инверсия внедрения зависимостей - чистый кодер» . sites.google.com . Проверено 18 июля 2015 .
  31. ^ «Отделение вашего приложения от инфраструктуры внедрения зависимостей» . InfoQ . Проверено 18 июля 2015 .
  32. ^ «Шаблон проектирования внедрения зависимостей - структура и взаимодействие» . w3sDesign.com . Проверено 12 августа 2017 .
  33. Мартин Фаулер (23 января 2004). «Инверсия управляющих контейнеров и шаблон внедрения зависимостей - формы внедрения зависимостей» . Martinfowler.com . Проверено 22 марта 2014 .
  34. ^ «Ян - Типы внедрения зависимостей» . Yan.codehaus.org. Архивировано из оригинала на 2013-08-18 . Проверено 11 декабря 2013 .
  35. ^ «AccessibleObject (Java Platform SE 7)» . docs.oracle.com . Проверено 18 июля 2015 .
  36. ^ Riehle, Dirk (2000), Рамки Дизайн: Роль моделирования подход (PDF) , Швейцарский федеральный технологический институт
  37. ^ «Весенние советы: POJO с аннотациями не является простым» . Архивировано из оригинала на 2015-07-15 . Проверено 18 июля 2015 .
  38. ^ «Аннотации в POJO - благо или проклятие? | Techtracer» . 2007-04-07 . Проверено 18 июля 2015 .
  39. ^ Pro Spring Динамические модули для сервисных платформ OSGi . АПресс. 2009-02-17. ISBN 9781430216124. Проверено 6 июля 2015 .
  40. ^ «Блог Captain Debug:« Соглашение по настройке »заходит слишком далеко?» . www.captaindebug.com . Проверено 18 июля 2015 .
  41. ^ Декер, Колин. «В чем проблема с @Inject? | Devlog Колина» . blog.cgdecker.com . Проверено 18 июля 2015 .
  42. ^ Морлинг, Гуннар (2012-11-18). «Кинжал - новая среда внедрения зависимостей Java» . Dagger - новая структура внедрения зависимостей Java - размышления программиста . Проверено 18 июля 2015 .
  43. ^ "Программа Java Community Process (SM) - JSR: запросы спецификации Java - деталь JSR № 330" . www.jcp.org . Проверено 18 июля 2015 .

Внешние ссылки [ править ]

  • Композиция Root Марка Земанна
  • Руководство для начинающих по внедрению зависимостей
  • Внедрение зависимостей и тестируемые объекты: разработка слабосвязанных и тестируемых объектов - Джереми Вайкоттен; Журнал доктора Добба , май 2006 г.
  • Шаблоны проектирования: внедрение зависимостей - журнал MSDN, сентябрь 2005 г.
  • Оригинальная статья Мартина Фаулера, в которой был введен термин внедрение зависимостей
  • P of EAA: плагин
  • Богатое инженерное наследие, лежащее в основе внедрения зависимостей - Эндрю Маквей - Подробная история внедрения зависимостей.
  • Что такое внедрение зависимостей? - Альтернативное объяснение - Якоб Дженков
  • Написание более тестируемого кода с внедрением зависимостей - Developer.com, октябрь 2006 г.
  • Обзор структуры управляемой расширяемости - MSDN
  • Старомодное описание механизма зависимости Ханта 1998 г.
  • Реорганизуйте свой путь к контейнеру внедрения зависимостей
  • Понимание DI в PHP
  • Вам не нужен контейнер для внедрения зависимостей