Перейти к содержимому

Dependency injection java что это

  • автор:

Как работает Dependency Injection (DI) в Spring Разработка приложений Java

В этой статье объясняется концепция Dependency Injection (DI) и как она работает в разработке Java-приложений Spring. Вы узнаете о преимуществах, недостатках и основах DI на примерах. Посмотрите дальше для получения дополнительной информации.

Преимущества внедрения зависимости

  • DI позволяет клиенту гибко настраиваться. Исправлено только поведение клиента.
  • Тестирование может быть выполнено с использованием фиктивных объектов.
  • Слабо пара архитектуры.
  • DI преимущества высокой когезии:
    • Уменьшенная сложность модуля
    • Повышение удобства обслуживания системы, поскольку изменения логики в домене влияют на меньшее количество модулей.
    • Увеличенное повторное использование модуля.

    Недостатки внедрения зависимости

    • DI создает клиентов, которые требуют настройки деталей, предоставленных строительным кодом.
    • DI может затруднить отслеживание кода, поскольку он отделяет поведение от конструкции; это означает, что разработчики обращаются к большему количеству файлов, чтобы следить за работой системы.
    • DI может вызвать взрыв типов, особенно в языках с явными типами интерфейса, таких как C # и Java.
    • DI может стимулировать зависимость от DI.
    • Тесная связь :
      • Изменение только в одном модуле обычно вызывает волновой эффект изменений в других модулях.

      Инъекция зависимости (DI)

      • Dependency Injection (DI) — это шаблон разработки программного обеспечения, который реализует инверсию управления для разрешения зависимостей.
      • Инъекция — это передача зависимости зависимому объекту, который будет ее использовать.
      • DI — это процесс, посредством которого объекты определяют свои зависимости. Другие объекты, с которыми они работают, — только через аргументы конструктора или аргументы метода или свойства фабрики — задаются в экземпляре объекта после его создания или возврата из метода фабрики.
      • Затем контейнер внедряет эти зависимости и создает компонент. Этот процесс называется Inversion of Control (IoC) (сам компонент управляет созданием или расположением своих зависимостей с помощью классов прямой конструкции или локатора служб).
      • DI относится к процессу предоставления внешней зависимости к программному компоненту.

      Внедрение зависимостей осуществляется двумя способами

      1. Конструкторское внедрение зависимостей

      • DI на основе конструктора — это когда контейнер вызывает конструктор с несколькими аргументами, каждый из которых представляет зависимость или другой класс.
      • Вызов статического метода фабрики с конкретными аргументами для создания bean-компонента примерно эквивалентен, обработка аргументов для конструктора и статического метода фабрики. В следующем примере показан класс, который может быть внедрен в зависимости только с помощью конструктора. Это POJO, который не зависит от конкретных интерфейсов контейнера, базовых классов или аннотаций.
      public class SimpleStudentList // the SimpleStudentList has a dependency on StudentFind private StudentFind studentFind; // a constructor that Spring container can 'inject' a StudentFind public SimpleStudentList(StudentFind studentFind ) this.studentFind = studentFind ; > // business logic code >
      Пример конструктора на основе DI
      Book.java
      package com.spring.example;  public class Book < private int id; private String bookName;  public Book()   public Book(int id)   public Book(String bookName) < this.bookName = bookName;>  public Book(int id, String bookName) < this.id = id; this.bookName = bookName; > void display() < System.out.println(id+" "+bookName); > > 
      applicationContext.xml
                 
      Main.java
      package com.spring.example;  import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.*;  public class Main < public static void main(String[] args) <  Resource r=new ClassPathResource("applicationContext.xml"); BeanFactory factory=new XmlBeanFactory(r);  Book b=(Book)factory.getBean("book"); b.display();  > > 
      Вывод:

      2. Установка на основе зависимостей

      DI на основе установки — это когда контейнер вызывает методы setter для ваших bean-компонентов после того, как он вызвал конструктор без аргументов или статический фабричный метод без аргументов для создания экземпляра этого bean-компонента.

      В следующем примере показан класс, который может иметь только чистую инъекцию сеттера.

      public class SimpleStudentList // the SimpleStudentList has a dependency on StudentFind private StudentFind studentFind; // a setter method that Spring container can 'inject' a StudentFind public void setStudentFind(StudentFind studentFind ) this.studentFind = studentFind ; > // business logic >
      Пример DI на основе сеттера
      Book.java
      package com.spring.example;  public class Book < private int id; private String bookName; private String author;  public int getId() < return id; > public void setId(int id) < this.id = id; > public String getBookName() < return bookName; > public void setBookName(String bookName) < this.bookName = bookName; >  public String getAuthor() < return author; > public void setAuthor(String author) < this.author = author; > void display() < System.out.println(id+" "+bookName+" "+author); > >
      applicationContext.xml
               1    The Complete Reference J2EE    Herbert Schildt      
      Main.java
      package com.spring.example;  import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.core.io.*;  public class Main < public static void main(String[] args) <  Resource r=new ClassPathResource("applicationContext.xml"); BeanFactory factory=new XmlBeanFactory(r);  Book b=(Book)factory.getBean("book"); b.display();  > > 
      Вывод :
      • Полный справочник J2EE Герберт Шильдт

      Наша команда разработчиков Java-приложений только что объяснила концепцию внедрения зависимостей, ее преимущества, недостатки и использование в Spring с примерами. Если у вас все еще есть путаница, сообщите нам и получите ответ от профессионалов.

      Dependency injection

      Представляемый вашему вниманию перевод открывает серию статей от Jakob Jenkov, посвященных внедрению зависимостей, или DI. Примечательна серия тем, что в ней автор, анализируя понятия и практическое применение таких понятий как «зависимость», «внедрение зависимостей», «контейнер для внедрения зависимостей», сравнивая паттерны создания объектов, анализируя недостатки конкретных реализаций DI-контейнеров (например, Spring), рассказывает, как пришел к написанию собственного DI-контейнера. Таким образом, читателю предлагается познакомиться с довольно цельным взглядом на вопрос управления зависимостями в приложениях.

      В данной статье сравнивается подход к настройке объектов изнутри и извне (DI). По смыслу настоящая статья продолжает статью Jakob Jenkov Understanding Dependencies, в которой дается определение самому понятию «зависимости» и их типам.

      Серия включает в себя следующие статьи
      • Understanding Dependencies
      1. Dependency Injection
      2. Dependency Injection Containers
      3. Dependency Injection Benefits
      4. When to use Dependency Injection
      5. Is Dependency Injection Replacing the Factory Patterns?

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

      «Внедрение зависимостей» — это выражение, впервые использованное в статье Мартина Фаулера Inversion of Control Containers and the Dependency Injection Pattern. Это хорошая статья, но она упускает из виду некоторые преимущества контейнеров внедрения зависимостей. Также я не согласен с выводами статьи, но об этом — в следующих текстах.

      Объяснение внедрения зависимостей

      Внедрение зависимостей — это стиль настройки объекта, при котором поля объекта задаются внешней сущностью. Другими словами, объекты настраиваются внешними объектами. DI — это альтернатива самонастройке объектов. Это может выглядеть несколько абстрактно, так что посмотрим пример:

      UPD: после обсуждения представленных автором фрагментов кода с flatscode и fogone, я принял решение скорректировать спорные моменты в коде. Изначальный замысел был в том, чтобы не трогать код и давать его таким, каков он написан автором. Оригинальный авторский код в спорных местах закомментирован с указанием «в оригинале», ниже дается его исправленная версия. Также оригинальный код можно найти по ссылке в начале статьи.

      public class MyDao < //в оригинале: protected DataSource dataSource = private DataSource dataSource = new DataSourceImpl("driver", "url", "user", "password"); //data access methods. public Person readPerson(int primaryKey) >

      Этот DAO (Data Access Object), MyDao нуждается в экземпляре javax.sql.DataSource для того, чтобы получить подключения к базе данных. Подключения к БД используются для чтения и записи в БД, например, объектов Person.

      Заметьте, что класс MyDao создает экземпляр DataSourceImpl, так как нуждается в источнике данных. Тот факт, что MyDao нуждается в реализации DataSource, означает, что он зависит от него. Он не может выполнить свою работу без реализации DataSource. Следовательно, MyDao имеет «зависимость» от интерфейса DataSource и от какой-то его реализации.

      Класс MyDao создает экземпляр DataSourceImpl как реализацию DataSource. Следовательно, класс MyDao сам «разрешает свои зависимости». Когда класс разрешает собственные зависимости, он автоматически также зависит от классов, для которых он разрешает зависимости. В данном случае MyDao завсист также от DataSourceImpl и от четырех жестко заданных строковых значений, передаваемых в конструктор DataSourceImpl. Вы не можете ни использовать другие значения для этих четырех строк, ни использовать другую реализацию интерфейса DataSource без изменения кода.

      Как вы можете видеть, в том случае, когда класс разрешает собственные зависимости, он становится негибким в отношении к этим зависимостям. Это плохо. Это значит, что если вам нужно поменять зависимости, вам нужно поменять код. В данном примере это означает, что если вам нужно использовать другую базу данных, вам потребуется поменять класс MyDao. Если у вас много DAO-классов, реализованных таким образом, вам придется изменять их все. В добавок, вы не можете провести юнит-тестирование MyDao, замокав реализацию DataSource. Вы можете использовать только DataSourceImpl. Не требуется много ума, чтобы понять, что это плохая идея.

      Давайте немного поменяем дизайн:

      public class MyDao < //в оригинале: protected DataSource dataSource = null; private final DataSource dataSource; public MyDao(String driver, String url, String user, String password)< this.dataSource = new DataSourceImpl(driver, url, user, password); >//data access methods. public Person readPerson(int primaryKey) >

      Заметьте, что создание экземпляра DataSourceImpl перемещено в конструктор. Конструктор принимает четыре параметра, это — четыре значения, необходимые для DataSourceImpl. Хотя класс MyDao все еще зависит от этих четырех значений, он больше не разрешает зависимости сам. Они предоставляются классом, создающим экземпляр MyDao. Зависимости «внедряются» в конструктор MyDao. Отсюда и термин «внедрение (прим. перев.: или иначе — инъекция) зависимостей». Теперь возможно сменить драйвер БД, URL, имя пользователя или пароль, используемый классом MyDao без его изменения.

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

      Класс MyDao может быть более независимым. Сейчас он все еще зависит и от интерфейса DataSource, и от класса DataSourceImpl. Нет необходимости зависеть от чего-то, кроме интерфейса DataSource. Это может быть достигнуто инъекцией DataSource в конструктор вместо четырех параметров строкового типа. Вот как это выглядит:

      public class MyDao < //в оригинале: protected DataSource dataSource = null; private final DataSource dataSource; public MyDao(DataSource dataSource)< this.dataSource = dataSource; >//data access methods. public Person readPerson(int primaryKey) >

      Теперь класс MyDao больше не зависит от класса DataSourceImpl или от четырех строк, необходимых конструктору DataSourceImpl. Теперь можно использовать любую реализацию DataSource в конструкторе MyDao.

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

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

      public class MyBizComponent < public void changePersonStatus(Person person, String status)< MyDao dao = new MyDao( new DataSourceImpl("driver", "url", "user", "password")); Person person = dao.readPerson(person.getId()); person.setStatus(status); dao.update(person); >>

      Как вы можете видеть, теперь MyBizComponent зависит от класса DataSourceImpl и четырех строк, необходимых его конструктору. Это еще хуже, чем зависимость MyDao от них, потому что MyBizComponent теперь зависит от классов и от информации, которую он сам даже не использует. Более того, реализация DataSourceImpl и параметры конструктора принадлежат к разным слоям абстракции. Слой ниже MyBizComponent — это слой DAO.

      Решение — продолжить внедрение зависимости по всем слоям. MyBizComponent должен зависеть только от экземпляра MyDao. Вот как это выглядит:

       public class MyBizComponent < //в оригинале: protected MyDao dao = null; private final MyDao dao; public MyBizComponent(MyDao dao)< this.dao = dao; >public void changePersonStatus(Person person, String status) < Person person = dao.readPerson(person.getId()); person.setStatus(status); dao.update(person); >>

      Снова зависимость, MyDao, предоставляется через конструктор. Теперь MyBizComponent зависит только от класса MyDao. Если бы MyDao был интерфейсом, можно было бы менять реализацию без ведома MyBizComponent.

      Такой паттерн внедрения зависимости должен продолжается через все слои приложения, с самого нижнего слоя (слоя доступа к данным) до пользовательского интерфейса (если он есть).

      • dependencies
      • dependency injection

      A quick intro to Dependency Injection: what it is, and when to use it

      A quick intro to Dependency Injection: what it is, and when to use it

      That’s the Wikipedia definition but still, but it’s not particularly easy to understand. So let’s understand it better.

      Before understanding what it means in programming, let’s first see what it means in general as it will help us understand the concept better.

      Dependency or dependent means relying on something for support. Like if I say we are relying too much on mobile phones than it means we are dependent on them.

      So before getting to dependency injections, first let’s understand what a dependency in programming means.

      When class A uses some functionality of class B, then its said that class A has a dependency of class B.

      1*0P-1JhnUaZeobDUAajIbhA

      In Java, before we can use methods of other classes, we first need to create the object of that class (i.e. class A needs to create an instance of class B).

      So, transferring the task of creating the object to someone else and directly using the dependency is called dependency injection.

      1*TF-VdAgPfcD497kAW77Ukg

      Why should I use dependency injection?

      Let’s say we have a car class which contains various objects such as wheels, engine, etc.

      Here the car class is responsible for creating all the dependency objects. Now, what if we decide to ditch MRFWheels in the future and want to use Yokohama Wheels?

      We will need to recreate the car object with a new Yokohama dependency. But when using dependency injection (DI), we can change the Wheels at runtime (because dependencies can be injected at runtime rather than at compile time).

      You can think of DI as the middleman in our code who does all the work of creating the preferred wheels object and providing it to the Car class.

      It makes our Car class independent from creating the objects of Wheels, Battery, etc.

      There are basically three types of dependency injection:
      1. constructor injection: the dependencies are provided through a class constructor.
      2. setter injection: the client exposes a setter method that the injector uses to inject the dependency.
      3. interface injection: the dependency provides an injector method that will inject the dependency into any client passed to it. Clients must implement an interface that exposes a setter method that accepts the dependency.

      So now its the dependency injection’s responsibility to:

      1. Create the objects
      2. Know which classes require those objects
      3. And provide them all those objects

      If there is any change in objects, then DI looks into it and it should not concern the class using those objects. This way if the objects change in the future, then its DI’s responsibility to provide the appropriate objects to the class.

      Inversion of control —the concept behind DI

      This states that a class should not configure its dependencies statically but should be configured by some other class from outside.

      It is the fifth principle of S.O.L.I.D — the five basic principles of object-oriented programming and design by Uncle Bob — which states that a class should depend on abstraction and not upon concretions (in simple terms, hard-coded).

      According to the principles, a class should concentrate on fulfilling its responsibilities and not on creating objects that it requires to fulfill those responsibilities. And that’s where dependency injection comes into play: it provides the class with the required objects.

      Note: If you want to learn about SOLID principles by Uncle Bob then you can head to this link.

      Benefits of using DI
      1. Helps in Unit testing.
      2. Boiler plate code is reduced, as initializing of dependencies is done by the injector component.
      3. Extending the application becomes easier.
      4. Helps to enable loose coupling, which is important in application programming.
      Disadvantages of DI
      1. It’s a bit complex to learn, and if overused can lead to management issues and other problems.
      2. Many compile time errors are pushed to run-time.
      3. Dependency injection frameworks are implemented with reflection or dynamic programming. This can hinder use of IDE automation, such as “find references”, “show call hierarchy” and safe refactoring.

      You can implement dependency injection on your own (Pure Vanilla) or use third-party libraries or frameworks.

      Libraries and Frameworks that implement DI
      • Spring (Java)
      • Google Guice (Java)
      • Dagger (Java and Android)
      • Castle Windsor (.NET)
      • Unity(.NET)

      To learn more about dependency injection, you can check out the below resources:

      If you liked the article and want to read more amazing articles, then do follow me here (Bhavya Karia) and show your support as it motivates me to write more.

      If you have any questions or feedback for me than let’s connect on LinkedIn, Twitter , Facebook.

      Edit 1:

      Thanks to Sergey Ufocoder now this article has been converted into the Russian language. My Russian friends and who all can read the Russian language do give it a read.

      Also, if you want to apply DI in JavaScript and are looking for a library then Jo Surikat suggests that you give a try to his library.

      One more awesome DI library in JavaScript was suggested by Nicolas Froidure.

      Edit 2:

      If you are a PHP developer then don’t worry, got you all covered as well. Gordon Forsythe recommended this amazing library which you all might want to try out.

      Thanks for all the kind words that I have been receiving. Do share the article so that more and more people can be benefited.

      If you learnt even a thing or two, please share this story!

      Основы внедрения зависимостей

      Основы внедрения зависимостей

      В этой статье я расскажу об основах внедрения зависимостей (англ. Dependency Injection, DI) простым языком, а также расскажу о причинах использования этого подхода. Эта статья предназначена для тех, кто не знает, что такое внедрение зависимостей, или сомневается в необходимости использования этого приёма. Итак, начнём.

      Что такое зависимость?

      Давайте сначала изучим пример. У нас есть ClassA , ClassB и ClassC , как показано ниже:

      class ClassA < var classB: ClassB >class ClassB < var classC: ClassC >class ClassC

      Вы можете увидеть, что класс ClassA содержит экземпляр класса ClassB , поэтому мы можем сказать, что класс ClassA зависит от класса ClassB . Почему? Потому что классу ClassA нужен класс ClassB для корректной работы. Мы также можем сказать, что класс ClassB является зависимостью класса ClassA .

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

      Как работать с зависимостями?

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

      Первый способ: создавать зависимости в зависимом классе

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

      class ClassA < var classB: ClassB fun someMethodOrConstructor() < classB = ClassB() classB.doSomething() >>

      Это очень просто! Мы создаем класс, когда нам это необходимо.

      Преимущества

      • Это легко и просто.
      • Зависимый класс ( ClassA в нашем случае) полностью контролирует, как и когда создавать зависимости.

      Недостатки

      • ClassA и ClassB тесно связаны друг с другом. Поэтому всякий раз, когда нам нужно использовать ClassA , мы будем вынуждены использовать и ClassB , и заменить ClassB чем-то другим будет невозможно.
      • При любом изменении в инициализации класса ClassB потребуется корректировать код и внутри класса ClassA (и всех остальных зависимых от ClassB классов). Это усложняет процесс изменения зависимости.
      • ClassA невозможно протестировать. Если вам необходимо протестировать класс, а ведь это один из важнейших аспектов разработки ПО, то вам придётся проводить модульное тестирование каждого класса в отдельности. Это означает, что если вы захотите проверить корректность работы исключительно класса ClassA и создадите для его проверки несколько модульных тестов, то, как это было показано в примере, вы в любом случае создадите и экземпляр класса ClassB , даже когда он вас не интересует. Если во время тестирования возникает ошибка, то вы не сможете понять, где она находится — в ClassA или ClassB . Ведь есть вероятность, что часть кода в ClassB привела к ошибке, в то время как ClassA работает правильно. Другими словами, модульное тестирование невозможно, потому что модули (классы) не могут быть отделены друг от друга.
      • ClassA должен быть сконфигурирован таким образом, чтобы он мог внедрять зависимости. В нашем примере он должен знать, как создать ClassC и использовать его для создания ClassB . Лучше бы он ничего об этом не знал. Почему? Из-за принципа единой ответственности.

      Каждый класс должен выполнять лишь свою работу.

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

      Второй способ: внедрять зависимости через пользовательский класс

      Итак, понимая, что внедрение зависимостей внутри зависимого класса — не самая лучшая идея, давайте изучим альтернативный способ. Здесь зависимый класс определяет все необходимые ему зависимости внутри конструктора и позволяет пользовательскому классу предоставлять их. Является ли такой способ решением нашей проблемы? Узнаем немного позже.

      Посмотрите на пример кода ниже:

      class ClassA < var classB: ClassB constructor(classB: ClassB)< this.classB = classB >> class ClassB < var classC: ClassC constructor(classC: ClassC)< this.classC = classC >> class ClassC < constructor()< >> class UserClass() < fun doSomething()< val classC = ClassC(); val classB = ClassB(classC); val classA = ClassA(classB); classA.someMethod(); >> view rawDI Example In Medium - 

      Теперь ClassA получает все зависимости внутри конструктора и может просто вызывать методы класса ClassB , ничего не инициализируя.

      Преимущества

      • ClassA и ClassB теперь слабо связаны, и мы можем заменить ClassB , не нарушая код внутри ClassA . Например, вместо передачи ClassB мы сможем передать AssumeClassB , который является подклассом ClassB , и наша программа будет исправно работать.
      • ClassA теперь можно протестировать. При написании модульного теста, мы можем создать нашу собственную версию ClassB (тестовый объект) и передать её в ClassA . Если возникает ошибка во время прохождения теста, то теперь мы точно знаем, что это определенно ошибка в ClassA .
      • ClassB освобожден от работы с зависимостями и может сосредоточиться на выполнении своих задач.

      Недостатки

      • Этот способ напоминает цепной механизм, и в какой-то момент цепь должна прерваться. Другими словами, пользователь класса ClassA должен знать всё об инициализации ClassB , что в свою очередь требует знаний и об инициализации ClassC и т.д. Итак, вы видите, что любое изменение в конструкторе любого из этих классов может привести к изменению вызывающего класса, не говоря уже о том, что ClassA может иметь больше одного пользователя, поэтому логика создания объектов будет повторяться.
      • Несмотря на то, что наши зависимости ясны и просты для понимания, пользовательский код нетривиален и сложен в управлении. Поэтому всё не так просто. Кроме того, код нарушает принцип единой ответственности, поскольку отвечает не только за свою работу, но и за внедрение зависимостей в зависимые классы.

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

      Что такое внедрение зависимостей?

      Внедрение зависимостей — это способ обработки зависимостей вне зависимого класса, когда зависимому классу не нужно ничего делать.

      Исходя из этого определения, наше первое решение явно не использует идею внедрения зависимостей, а второй способ заключается в том, что зависимый класс ничего не делает для предоставления зависимостей. Но мы все ещё считаем второе решение плохим. ПОЧЕМУ?!

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

      Как же сделать лучше? Давайте рассмотрим третий способ обработки зависимостей.

      Третий способ: пусть кто-нибудь ещё обрабатывает зависимости вместо нас

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

      «Чистая» реализация внедрения зависимостей (по моему личному мнению)

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

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

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

      Во-первых, данные фреймворки предлагают способ определения полей (объектов), которые должны быть внедрены. Некоторые фреймворки осуществляют это посредством аннотирования поля или конструктора с помощью аннотации @Inject , но существуют и другие методы. Например, Koin использует встроенные языковые особенности Kotlin для определения внедрения. Под Inject подразумевается, что зависимость должна обрабатываться DI-фреймворком. Код будет выглядеть примерно так:

      class ClassA < var classB: ClassB @Inject constructor(classB: ClassB)< this.classB = classB >> class ClassB < var classC: ClassC @Inject constructor(classC: ClassC)< this.classC = classC >> class ClassC < @Inject constructor()< >>

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

      class OurThirdPartyGuy < fun provideClassC()< return ClassC() //just creating an instance of the object and return it. >fun provideClassB(classC: ClassC) < return ClassB(classC) >fun provideClassA(classB: ClassB) < return ClassA(classB) >>

      Итак, как вы видите, каждая функция отвечает за обработку одной зависимости. Поэтому если нам где-то в приложении нужно использовать ClassA , то произойдет следующее: наш DI-фреймворк создаёт один экземпляр класса ClassC , вызвав provideClassC , передав его в provideClassB и получив экземпляр ClassB , который передаётся в provideClassA , и в результате создаётся ClassA . Это практически волшебство. Теперь давайте изучим преимущества и достоинства третьего способа.

      Преимущества

      • Все максимально просто. И зависимый класс, и класс, предоставляющий зависимости, понятны и просты.
      • Классы слабо связаны и легко заменяемы другими классами. Допустим, мы хотим заменить ClassC на AssumeClassC , который является подклассом ClassC . Для этого нужно лишь изменить код провайдера следующим образом, и везде, где используется ClassC , теперь автоматически будет использоваться новая версия:
      fun provideClassC()

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

      • Невероятная тестируемость. Можно легко заменить зависимости тестовыми версиями во время тестирования. Фактически, внедрение зависимостей — ваш главный помощник, когда речь заходит о тестировании.
      • Улучшение структуры кода, т.к. в приложении есть отдельное место для обработки зависимостей. В результате остальные части приложения могут сосредоточиться на выполнении исключительно своих функций и не пересекаться с зависимостями.

      Недостатки

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

      Заключение

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

      В этой статье я попытался объяснить основы работы с понятием внедрения зависимостей, а также перечислил причины необходимости использования этой идеи. Существует ещё множество ресурсов, которые вы можете изучить, чтобы больше узнать о применении DI в ваших собственных приложениях. Например, этой теме посвящён отдельный раздел в продвинутой части нашего курса Android-профессии.

      • android development
      • android
      • андроид
      • внедрение зависимостей
      • dependency injection
      • DI
      • kotlin
      • котлин
      • тестирование
      • testing
      • перевод с английского
      • программирование
      • разработка
      • devcolibri
      • никто не читает теги
      • Программирование
      • Разработка мобильных приложений
      • Разработка под Android
      • Тестирование мобильных приложений
      • Kotlin

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *