Внедрение зависимости
Внедрение зависимости (англ. Dependency injection) обозначает процесс предоставления внешней зависимости программному компоненту и является специфичной формой «обращения контроля (англ. Inversion of control)», где изменение порядка связи является путём получения необходимой зависимости.
Условно, если объекту нужно получить доступ к определенному сервису, объект берет на себя ответственность за доступ к этому сервису: он или получает прямую ссылку на местонахождение сервиса, или обращается к известному «сервис-локатору» и запрашивает ссылку на реализацию определенного типа сервиса. Используя же внедрение зависимости, объект просто предоставляет свойство, которое в состоянии хранить ссылку на нужный тип сервиса; и когда объект создается, ссылка на реализацию нужного типа сервиса автоматически вставляется в это свойство (поле), используя средства среды. Внедрение зависимости более гибко, потому как становится легче создавать альтернативные реализации данного типа сервиса, а потом указывать, какая именно реализация должна быть использована в, например, конфигурационном файле, без изменений в объектах, которые этот сервис используют. Это особенно полезно в юнит-тестировании, потому как вставить реализацию «заглушки» сервиса в тестируемый объект очень просто. С другой стороны, излишнее использование внедрения зависимостей может сделать приложения более сложными и трудными в сопровождении: так как для понимания поведения программы программисту необходимо смотреть не только в исходный код, а еще и в конфигурацию, а конфигурация, как правило, невидима для IDE, которые поддерживают анализ ссылок и рефакторинг, если явно не указана поддержка фреймворков с внедрениями зависимостей.
Примеры кода
Представим, что IFoo
это общий класс, определяющий свой интерфейс:
class IFoo(Interface): def do_bar(): "perform bar" def do_baz(): "perform baz"
Также есть несколько реализаций, которые реализуют IFoo по разному:
class DatabaseFoo(object): interface.implements(IFoo) def do_bar(): db.select_bar().execute() # Использовать базу данных, чтобы сделать bar def do_baz(): db.select_baz().run() # Использовать базу данных, чтобы сделать baz
class PixieDustFoo(object): interface.implements(IFoo) def do_bar(): cast_spell("bar") # Магия... def do_baz(): cast_spell("baz") # ...и волшебство
class EnterpriseFoo(object): interface.implements(IFoo) def do_bar(): bar = EnterpriseFactoryObserverFactoryCreator("bar") bar.creatify() bar.preparify() bar.configurise() bar.make_award_winning() bar.opportunities.leverage() def do_baz(): baz = EnterpriseFactoryObserverFactoryCreator("baz") baz.creatify() baz.preparify() baz.configurise() baz.make_award_winning() baz.opportunities.leverage()
IFoo
определяет только операции, которые доступны в интерфейсе, но не предоставляет никакой их реализации, позволяя другим классам реализовывать методы, в нем определенные (на то он и интерфейс). Поэтому пользователь, желающий использовать функционал IFoo, может использовать любую реализацию, не имея никакого понятия о том, как оно работает, кроме того, что они соответствуют спецификации интерфейса Foo.
class ImportantClass(object): def __init__(): self.foo = EnterpriseFoo() def do_really_important_stuff(): self.foo.do_bar() # оно ничего не делает!
Хотя, это подрывает весь смысл использования интерфейсов вместо конечных реализаций. Чтобы исправить ситуацию, достаточно предоставить вызывающей стороне возможность указывать ту реализацию IFoo, которую она посчитает нужным:
def __init__(self, foo): self.foo = foo
При использовании внедрения зависимостей, как правило, существует конфигурационный механизм или архитектура, которая определяет целесообразность выбора той или иной реализации в зависимости от поставленных целей.
Существующие фреймворки
Реализация внедрения зависимостей существует для различных платформ и языков, включая:
ActionScript
C++
ColdFusion
Java
- Seasar
- Spring Framework
- J2EE 5 / EJB 3
- Naked objects
- miocc — Microscopic Inversion of Control Container
.NET
- Autofac
- Castle MicroKernel/Windsor
- ObjectBuilder
- PicoContainer.NET
- Puzzle.NFactory
- Spring.NET
- StructureMap
- Ninject
- Unity
PHP4
PHP5
Perl
Python
Ruby
См. также
Внешние ссылки
- 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
- Domain Specific Modeling (DSM) in IoC frameworks
- Does Dependency Injection pay off? — InfoQ
- Dependency Injection — Dhanji R. Prasanna; Manning Publications, 2008.
de:Dependency Injection en:Dependency injection es:Inyección de dependencias fr:Injection de dépendances ja:依存性の注入 pl:Wstrzykiwanie zależności pt:Injeção de dependência
Если вам нравится SbUP.com Сайт, вы можете поддержать его - BTC: bc1qppjcl3c2cyjazy6lepmrv3fh6ke9mxs7zpfky0 , TRC20 и ещё....