Update "What is What is dependency injection?" documentation page

This commit is contained in:
Roman Mogylatov 2020-09-03 23:09:21 -04:00
parent 2cf5efa031
commit 14c2ecae8f
4 changed files with 188 additions and 98 deletions

View File

@ -57,14 +57,25 @@ It helps implementing the dependency injection principle.
What is dependency injection? What is dependency injection?
----------------------------- -----------------------------
Dependency injection is a principle that helps to decrease coupling and increase cohesion. Your Dependency injection is a principle that helps to decrease coupling and increase cohesion.
code becomes more flexible, clear and it is easier to test it.
What is coupling and cohesion?
Coupling and cohesion are about how tough the components are tied.
- **High coupling**. If the coupling is high it's like using a superglue or welding. No easy way
to disassemble.
- **High cohesion**. High cohesion is like using the screws. Very easy to disassemble and
assemble back or assemble a different way. It is an alternative to high coupling.
When the cohesion is high the coupling is low.
High cohesion brings the flexibility. Your code becomes easier to change and to test.
How to implement dependency injection? How to implement dependency injection?
-------------------------------------- --------------------------------------
Objects do not create each other anymore. They provide a way to inject the needed dependencies Objects do not create each other anymore. They provide a way to inject the dependencies instead.
instead.
Before: Before:
@ -114,14 +125,22 @@ After:
service = Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT'))) service = Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT')))
Flexibility comes with a price: now you need to assemble your objects like this ``ApiClient`` is decoupled from knowing where the options come from. You can read a key and a
timeout from a configuration file or even get them from a database.
``Service`` is decoupled from the ``ApiClient``. It does not create it anymore. You can provide a
stub or other compatible object.
Flexibility comes with a price.
Now you need to assemble your objects like this
``Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT')))``. The assembly code might get ``Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT')))``. The assembly code might get
duplicated and it'll become harder to change the application structure. duplicated and it'll become harder to change the application structure.
What does Dependency Injector do? What does Dependency Injector do?
--------------------------------- ---------------------------------
``Dependency Injector`` helps you assemble the objects. ``Dependency Injector`` helps to assemble the objects.
It provides you the container and the providers that help you describe objects assembly. When you It provides you the container and the providers that help you describe objects assembly. When you
need an object you get it from the container. The rest of the assembly work is done by the need an object you get it from the container. The rest of the assembly work is done by the
@ -170,8 +189,11 @@ framework:
Retrieving of the ``Service`` instance now is done like this ``container.service()``. Retrieving of the ``Service`` instance now is done like this ``container.service()``.
Also ``Dependency Injector`` provides a bonus in overriding any of the providers with the The responsibility of assembling the object is consolidated in the container. When you need to
``.override()`` method: make a change you do it in one place.
When doing the testing you call the ``container.api_client.override()`` to replace the real API
client with a mock:
.. code-block:: python .. code-block:: python
@ -180,7 +202,6 @@ Also ``Dependency Injector`` provides a bonus in overriding any of the providers
with container.api_client.override(mock.Mock()): with container.api_client.override(mock.Mock()):
service = container.service() service = container.service()
assert isinstance(service.api_client, mock.Mock)
It helps in a testing. Also you can use it for configuring project for the different environments: It helps in a testing. Also you can use it for configuring project for the different environments:
replace an API client with a stub on the dev or stage. replace an API client with a stub on the dev or stage.

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@ -1,125 +1,190 @@
What is dependency injection and inversion of control? What is dependency injection?
------------------------------------------------------ -----------------------------
.. meta:: .. meta::
:keywords: Python,DI,Dependency injection,IoC,Inversion of Control :keywords: Python,DI,Dependency injection,Low coupling,High cohesion
:description: This article provides definition of dependency injection, :description: This page provides a Python example of what is dependency injection. It tells
inversion of control and dependency inversion. It contains about benefits of coupling and high cohesion.
example code in Python that is refactored to be following
inversion of control principle.
Definition Dependency injection is a principle that helps to decrease coupling and increase cohesion.
~~~~~~~~~~
Wikipedia provides quite good definitions of dependency injection pattern .. image:: images/coupling-cohesion.png
and related principles:
.. glossary:: What is coupling and cohesion?
`Dependency injection`_ Coupling and cohesion are about how tough the components are tied.
In software engineering, dependency injection is a software design
pattern that implements inversion of control for resolving
dependencies. A dependency is an object that can be used (a service).
An injection is the passing of a dependency to a dependent object (a
client) that would use it. The service is made part of the client's
state. Passing the service to the client, rather than allowing a
client to build or find the service, is the fundamental requirement of
the pattern.
Dependency injection allows a program design to follow the dependency - **High coupling**. If the coupling is high it's like using a superglue or welding. No easy way
inversion principle. The client delegates to external code (the to disassemble.
injector) the responsibility of providing its dependencies. The client - **High cohesion**. High cohesion is like using the screws. Very easy to disassemble and
is not allowed to call the injector code. It is the injecting code assemble back or assemble a different way. It is an alternative to high coupling.
that constructs the services and calls the client to inject them. This
means the client code does not need to know about the injecting code.
The client does not need to know how to construct the services. The
client does not need to know which actual services it is using. The
client only needs to know about the intrinsic interfaces of the
services because these define how the client may use the services.
This separates the responsibilities of use and construction.
`Inversion of control`_ When the cohesion is high the coupling is low.
In software engineering, inversion of control (IoC) describes a design
in which custom-written portions of a computer program receive the
flow of control from a generic, reusable library. A software
architecture with this design inverts control as compared to
traditional procedural programming: in traditional programming, the
custom code that expresses the purpose of the program calls into
reusable libraries to take care of generic tasks, but with inversion
of control, it is the reusable code that calls into the custom, or
task-specific, code.
Inversion of control is used to increase modularity of the program and High cohesion brings the flexibility. Your code becomes easier to change and to test.
make it extensible, and has applications in object-oriented
programming and other programming paradigms. The term was popularized
by Robert C. Martin and Martin Fowler.
The term is related to, but different from, the dependency inversion The example
principle, which concerns itself with decoupling dependencies between ~~~~~~~~~~~
high-level and low-level layers through shared abstractions.
`Dependency inversion`_ How does dependency injection helps to achieve high cohesion?
In object-oriented programming, the dependency inversion principle
refers to a specific form of decoupling software modules. When
following this principle, the conventional dependency relationships
established from high-level, policy-setting modules to low-level,
dependency modules are reversed, thus rendering high-level modules
independent of the low-level module implementation details. The
principle states:
+ High-level modules should not depend on low-level modules. Objects do not create each other anymore. They provide a way to inject the dependencies instead.
Both should depend on abstractions.
+ Abstractions should not depend on details.
Details should depend on abstractions.
The principle inverts the way some people may think about Before:
object-oriented design, dictating that both high- and low-level
objects must depend on the same abstraction.
Example .. code-block:: python
~~~~~~~
Let's go through the code of ``example.py``: import os
.. literalinclude:: ../../examples/di_demo/example.py
:language: python
At some point, things defined above mean, that the code from ``example.py``, class ApiClient:
could look different, like in ``example_di.py``:
.. literalinclude:: ../../examples/di_demo/example_di.py def __init__(self):
:language: python self.api_key = os.getenv('API_KEY') # <-- the dependency
self.timeout = os.getenv('TIMEOUT') # <-- the dependency
Best explanation, ever
~~~~~~~~~~~~~~~~~~~~~~
Some times ago `user198313`_ posted awesome `question`_ about dependency class Service:
injection on `StackOverflow`_:
.. note:: def __init__(self):
self.api_client = ApiClient() # <-- the dependency
if __name__ == '__main__':
service = Service()
After:
.. code-block:: python
import os
class ApiClient:
def __init__(self, api_key: str, timeout: int):
self.api_key = api_key # <-- the dependency is injected
self.timeout = timeout # <-- the dependency is injected
class Service:
def __init__(self, api_client: ApiClient):
self.api_client = api_client # <-- the dependency is injected
if __name__ == '__main__':
service = Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT')))
``ApiClient`` is decoupled from knowing where the options come from. You can read a key and a
timeout from a configuration file or even get them from a database.
``Service`` is decoupled from the ``ApiClient``. It does not create it anymore. You can provide a
stub or other compatible object.
Flexibility comes with a price.
Now you need to assemble your objects like this
``Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT')))``. The assembly code might get
duplicated and it'll become harder to change the application structure.
Here comes the ``Dependency Injector``.
``Dependency Injector`` helps to assemble the objects.
It provides you the container and the providers that help you describe objects assembly. When you
need an object you get it from the container. The rest of the assembly work is done by the
framework:
.. code-block:: python
from dependency_injector import containers, providers
class ApiClient:
def __init__(self, api_key: str, timeout: int):
self.api_key = api_key
self.timeout = timeout
class Service:
def __init__(self, api_client: ApiClient):
self.api_client = api_client
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
api_client = providers.Singleton(
ApiClient,
api_key=config.api_key,
timeout=config.timeout.as_int(),
)
service = providers.Factory(
Service,
api_client=api_client,
)
if __name__ == '__main__':
container = Container()
container.config.api_key.from_env('API_KEY')
container.config.timeout.from_env('TIMEOUT')
service = container.service()
Retrieving of the ``Service`` instance now is done like this ``container.service()``.
Objects assembling is consolidated in the container. When you need to make a change you do it in
one place.
When doing the testing you call the ``container.api_client.override()`` to replace the real API
client with a mock:
.. code-block:: python
from unittest import mock
with container.api_client.override(mock.Mock()):
service = container.service()
How to explain dependency injection to a 5-year-old? How to explain dependency injection to a 5-year-old?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
And `John Munsch`_ provided absolutely Great answer: Some time ago `user198313`_ posted this `question`_ on the `StackOverflow`_.
.. note:: `John Munsch`_ provided a great answer:
When you go and get things out of the refrigerator for yourself, you can *When you go and get things out of the refrigerator for yourself, you can
cause problems. You might leave the door open, you might get something cause problems. You might leave the door open, you might get something
Mommy or Daddy doesn't want you to have. You might even be looking for Mommy or Daddy doesn't want you to have. You might even be looking for
something we don't even have or which has expired. something we don't even have or which has expired.*
What you should be doing is stating a need, "I need something to drink *What you should be doing is stating a need, "I need something to drink
with lunch," and then we will make sure you have something when you sit with lunch," and then we will make sure you have something when you sit
down to eat. down to eat.*
What's next?
~~~~~~~~~~~~
Choose one of the following as a next step:
+ Pass one of the tutorials:
+ :ref:`cli-tutorial`
+ :ref:`flask-tutorial`
+ :ref:`aiohttp-tutorial`
+ :ref:`asyncio-daemon-tutorial`
+ Know more about the :ref:`providers`
+ Go to the :ref:`contents`
.. disqus:: .. disqus::
.. _Dependency injection: http://en.wikipedia.org/wiki/Dependency_injection
.. _Inversion of control: https://en.wikipedia.org/wiki/Inversion_of_control
.. _Dependency inversion: https://en.wikipedia.org/wiki/Dependency_inversion_principle
.. _StackOverflow: http://stackoverflow.com/ .. _StackOverflow: http://stackoverflow.com/
.. _question: http://stackoverflow.com/questions/1638919/how-to-explain-dependency-injection-to-a-5-year-old/1639186 .. _question: http://stackoverflow.com/questions/1638919/how-to-explain-dependency-injection-to-a-5-year-old/1639186
.. _user198313: http://stackoverflow.com/users/198313/user198313 .. _user198313: http://stackoverflow.com/users/198313/user198313

View File

@ -7,6 +7,10 @@ that were made in every particular version.
From version 0.7.6 *Dependency Injector* framework strictly From version 0.7.6 *Dependency Injector* framework strictly
follows `Semantic versioning`_ follows `Semantic versioning`_
Development version
-------------------
- Update "What is What is dependency injection?" documentation page.
3.37.0 3.37.0
------ ------
- Update index documentation page. - Update index documentation page.