Внедрение зависимости — Википедия
Внедрение зависимости (англ. Dependency injection, DI) — процесс предоставления внешней зависимости программному компоненту. Является специфичной формой «инверсии управления» (англ. Inversion of control, IoC), когда она применяется к управлению зависимостями. В полном соответствии с принципом единственной ответственности объект отдаёт заботу о построении требуемых ему зависимостей внешнему, специально предназначенному для этого общему механизму[1].
Настоящее внедрение зависимости
[править | править код]При использовании паттерна «внедрение зависимости» объект пассивен и не предпринимает вообще никаких шагов для выяснения зависимостей, а предоставляет для этого сеттеры и/или принимает своим конструктором аргументы, посредством которых внедряются зависимости[1].
Принцип работы
[править | править код]![]() | В разделе не хватает ссылок на источники (см. рекомендации по поиску). |
Работа фреймворка, обеспечивающая внедрение зависимости, описывается следующим образом. Приложение, независимо от оформления, исполняется внутри контейнера IoC, предоставляемого фреймворком. Часть объектов в программе по-прежнему создается обычным способом языка программирования, часть создается контейнером на основе предоставленной ему конфигурации.
Условно, если объекту нужно получить доступ к определенному сервису, объект берет на себя обязанность по доступу к этому сервису: он или получает прямую ссылку на местонахождение сервиса, или обращается к известному «сервис-локатору» и запрашивает ссылку на реализацию определенного типа сервиса. Используя же внедрение зависимости, объект просто предоставляет свойство, которое в состоянии хранить ссылку на нужный тип сервиса; и когда объект создается, ссылка на реализацию нужного типа сервиса автоматически вставляется в это свойство (поле), используя средства среды.
Внедрение зависимости более гибко, потому что становится легче создавать альтернативные реализации данного типа сервиса, а потом указывать, какая именно реализация должна быть использована в, например, конфигурационном файле, без изменений в объектах, которые этот сервис используют. Это особенно полезно в юнит-тестировании, потому что вставить реализацию «заглушки» сервиса в тестируемый объект очень просто.
С другой стороны, излишнее использование внедрения зависимостей может сделать приложения более сложными и трудными в сопровождении: так как для понимания поведения программы программисту необходимо смотреть не только в исходный код, а еще и в конфигурацию, а конфигурация, как правило, невидима для IDE, которые поддерживают анализ ссылок и рефакторинг, если явно не указана поддержка фреймворков с внедрениями зависимостей.
Примеры кода
[править | править код]При использовании внедрения зависимостей, как правило, существует конфигурационный механизм или архитектура, которая определяет целесообразность выбора той или иной реализации в зависимости от поставленных целей.
Пример кода на PHP
<?php /** * Класс конфигурация базы данных */ class DbConfiguration { private $host; private $port; private $username; private $password; public function __construct(string $host, int $port, string $username, string $password) { // вся суть Di находится в строчках ниже $this->host = $host; $this->port = $port; $this->username = $username; $this->password = $password; } public function getHost() { return $this->host; } public function getPort() { return $this->port; } public function getUsername() { return $this->username; } public function getPassword() { return $this->password; } } /** * Класс соединение с базой данных */ class DbConnection { private $configuration; public function __construct(DbConfiguration $config) { // вся суть Di находится в строчке ниже $this->configuration = $config; } public function getDsn() { // примечание: это не реальный dsn, разделители в реальном dsn другие return sprintf( '%s:%s@%s:%d', $this->configuration->getUsername(), $this->configuration->getPassword(), $this->configuration->getHost(), $this->configuration->getPort() ); } } // создаем объект конфигурации базы данных, передавая в конструктор параметры $config = new DbConfiguration('localhost', 3306, 'username', 'password'); // создаем объект соединения с базой, посылая в конструктор объект конфигурации // использование Di дает слабосвязность кода $connection = new DbConnection($config);
Пример кода на Java
public interface ICar { public float getSpeed(); public void setPedalPressure(final float PEDAL_PRESSURE); } public interface IEngine { public float getEngineRotation(); public void setFuelConsumptionRate(final float FUEL_FLOW); }
Без использования dependency injection
public class DefaultEngineImpl implements IEngine { private float engineRotation = 0; public float getEngineRotation() { return engineRotation; } public void setFuelConsumptionRate(final float FUEL_FLOW) { engineRotation = …; } } public class DefaultCarImpl implements ICar { private IEngine engine = new DefaultEngineImpl(); public float getSpeed() { return engine.getEngineRotation()*…; } public void setPedalPressure(final float PEDAL_PRESSURE) { engine.setFuelConsumptionRate(…); } } public class MyApplication { public static void main(String[] args) { DefaultCarImpl car = new DefaultCarImpl(); car.setPedalPressure(5); float speed = car.getSpeed(); System.out.println("Speed of the car is " + speed); } }
Внедрение зависимости вручную
public class DefaultCarImpl implements ICar { private IEngine engine; public DefaultCarImpl(final IEngine engineImpl) { engine = engineImpl; } public float getSpeed() { return engine.getEngineRotation()*…; } public void setPedalPressure(final float PEDAL_PRESSURE) { engine.setFuelConsumptionRate(…); } } public class CarFactory { public static ICar buildCar() { return new DefaultCarImpl(new DefaultEngineImpl()); } } public class MyApplication { public static void main(String[] args) { ICar car = CarFactory.buildCar(); car.setPedalPressure(5); float speed = car.getSpeed(); System.out.println("Speed of the car is " + speed); } }
Внедрение зависимости при помощи фреймворка
<service-point id="CarBuilderService"> <invoke-factory> <construct class="Car"> <service>DefaultCarImpl</service> <service>DefaultEngineImpl</service> </construct> </invoke-factory> </service-point>
/** Неявная реализация **/ public class MyApplication { public static void main(String[] args) { Service service = (Service)DependencyManager.get("CarBuilderService"); ICar car = (ICar)service.getService(Car.class); car.setPedalPressure(5); float speed = car.getSpeed(); System.out.println("Speed of the car is " + speed); } }
См. также
[править | править код]Примечания
[править | править код]- ↑ 1 2 Martin, 2008.
Литература
[править | править код]- Dependency Injection in .NET — Mark Seemann, Manning, 2011
- Внедрение зависимостей в .NET — Симан М., Питер, 2013.
- Внедрение зависимостей в .NET — Марк Симан, неофициальный перевод.
- Robert C. Martin. Clean Code: A Handbook of Agile Software Craftsmanship. — Pearson Education, 2008. — P. 157. — ISBN 978-0-13-608325-2.
Ссылки
[править | править код]- Inversion of Control Containers and the Dependency Injection pattern — Martin Fowler.
- Dependency Injection & Testable Objects: Designing loosely coupled and testable objects — Jeremy Weiskotten; Dr. Dobb's Journal, May 2006.
- Design Patterns: Dependency Injection — MSDN Magazine, September 2005
- Writing More Testable Code with Dependency Injection — Developer.com, October 2006 Архивная копия от 11 марта 2008 на Wayback Machine
- Domain Specific Modeling (DSM) in IoC frameworks
- Does Dependency Injection pay off? — InfoQ
- Dependency Injection — Dhanji R. Prasanna; Manning Publications, 2008.
Для улучшения этой статьи желательно:
|