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

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

Служба выполнена часть клиента государства . [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 содержит переменную члена службы, которая инициализируется конструктором 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  {  public  void  setService ( Сервисная  служба ); }// Класс клиента Открытый  класс  Client  реализует  ServiceSetter  {  // Внутренняя ссылка на службу, используемую этим клиентом.  частная  сервисная  служба ; // Устанавливаем службу, которую должен использовать этот клиент.  @Override  public  void  setService ( Сервисная  служба )  {  this . service  =  сервис ;  } }

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

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

// Конструктор- клиент ( служба  службы ,  служба  otherService )  {  if  ( service  ==  null )  {  throw  new  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  {  public  void  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 public  class  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).

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

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

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

Когда 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 . Зажим (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 через Interface Injection  _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. ^ Мартин Фаулер (2004-01-23). «Инверсия управляющих контейнеров и шаблон внедрения зависимостей - формы внедрения зависимостей» . 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
  • Вам не нужен контейнер для внедрения зависимостей