Update introduction docs

This commit is contained in:
Roman Mogilatov 2016-03-29 20:17:12 +03:00
parent 064a1653fd
commit 08b00d71d4
9 changed files with 258 additions and 99 deletions

View File

@ -1,4 +1,3 @@
=============================================================
Dependency Injector --- Python dependency injection framework
=============================================================
@ -14,7 +13,7 @@ Dependency Injector --- Python dependency injection framework
designed to be unified, developer's friendly tool for managing any kind
of Python objects and their dependencies in formal, pretty way.
Below is a list of some key features and points of *Dependency Injector*:
Key features of *Dependency Injector*:
- Easy, smart, pythonic style.
- Obvious, clear structure.
@ -22,8 +21,6 @@ Below is a list of some key features and points of *Dependency Injector*:
- Thread safety.
- Semantic versioning.
Main idea of *Dependency Injector* is to keep dependencies under control.
Status
------
@ -59,7 +56,7 @@ Contents
.. toctree::
:maxdepth: 2
main/introduction
main/introduction/index
main/installation
providers/index
catalogs/index
@ -67,4 +64,3 @@ Contents
api/index
main/feedback
main/changelog

View File

@ -1,93 +0,0 @@
Introduction
============
Before you have started with *Dependency Injector* framework and dependecy
injection, there are a couple of introduction notes that might be useful.
What is DI and why is it needed?
--------------------------------
Python ecosystem consists of a big amount of various libraries that contain
different classes and functions that could be used for applications
development. Each of them has its own role.
Modern Python applications are mostly the composition of well-known open
source systems / frameworks / libraries and some turnkey functionality.
When application goes bigger, its complexity and SLOC_ are also increased.
Being driven by SOLID_ (for example), developers often start to split
application's sources into not so big classes, functions and modules, that are
less complex, could be reused several times and so on... It always helps, but
there is another problem on the horizon.
The name of this problem is - "Dependency hell!". It sounds like "I have so
many classes and functions! They are great, now I can understand each of them,
but it is so hard to see the whole picture! How are they linked with each
other? What dependencies does this class have?". And this is a key question:
"What dependencies does certain class / function have?". To resolve this issues
developers have to go inside with IoC_ principles and implementation patterns.
One of such IoC_ implementation patterns is called `dependency injection`_.
Dependency injection in Python
------------------------------
Interesting but, dependency injection is not very popular topic in Python.
The things are so because Python is an awesome language. Your eyes are opened
and your hands are free while you are using Python. In practice this means that
you can do dependency injection in Python in quite an easy way because language
itself helps you to do this. At the same time, even the thins are so, you still
have to do some work. Another one 'minor' problem is that there are several
ways to do dependency injection container.
Key features
------------
*Dependency Injector* is a dependency injection framework for Python projects.
It was designed to be unified, developer's friendly tool for managing any kind
of Python objects and their dependencies in formal, pretty way.
Below is a list of some key features and points of *Dependency Injector*
framework:
- Easy, smart, pythonic style.
- Obvious, clear structure.
- Memory efficiency.
- Semantic versioning.
Main idea of *Dependency Injector* is to keep dependencies under control.
Main entities
-------------
Current section describes *Dependency Injector* main entities and their
interaction with each other.
.. image:: /images/internals.png
:width: 100%
:align: center
There are 3 main entities:
- Providers. Providers are strategies of accesing objects. For example,
:py:class:`dependency_injector.providers.Factory` creates new instance of
provided class every time it is called.
:py:class:`dependency_injector.providers.Singleton` creates provided
instance once and returns it on every next call. Providers could be
overridden by another providers. Base class is -
:py:class:`dependency_injector.providers.Provider`.
- Injections. Injections are instructions for making dependency injections
(there are several ways how they could be done). Injections are used mostly
by :py:class:`dependency_injector.providers.Factory` and
:py:class:`dependency_injector.providers.Singleton` providers, but
these are not only cases. Base class is -
:py:class:`dependency_injector.injections.Injection`.
- Catalogs. Catalogs are collections of providers. They are used for grouping
of providers by some principles. Base class is -
:py:class:`dependency_injector.catalogs.DeclarativeCatalog`.
.. _SLOC: http://en.wikipedia.org/wiki/Source_lines_of_code
.. _SOLID: http://en.wikipedia.org/wiki/SOLID_%28object-oriented_design%29
.. _IoC: http://en.wikipedia.org/wiki/Inversion_of_control
.. _dependency injection: http://en.wikipedia.org/wiki/Dependency_injection

View File

@ -0,0 +1,10 @@
Dependency Injection in Python
------------------------------
Interesting but, dependency injection is not very popular topic in Python.
The things are so because Python is an awesome language. Your eyes are opened
and your hands are free while you are using Python. In practice this means that
you can do dependency injection in Python in quite an easy way because language
itself helps you to do this. At the same time, even the thins are so, you still
have to do some work. Another one 'minor' problem is that there are several
ways to do dependency injection container.

View File

@ -0,0 +1,12 @@
Introduction
============
Before you have started with *Dependency Injector* framework and dependecy
injection, there are a couple of introduction notes that might be useful.
.. toctree::
:maxdepth: 2
what_is_di
di_in_python

View File

@ -0,0 +1,45 @@
Key features
------------
*Dependency Injector* is a dependency injection framework for Python projects.
It was designed to be unified, developer's friendly tool for managing any kind
of Python objects and their dependencies in formal, pretty way.
Below is a list of some key features and points of *Dependency Injector*
framework:
- Easy, smart, pythonic style.
- Obvious, clear structure.
- Memory efficiency.
- Semantic versioning.
Main idea of *Dependency Injector* is to keep dependencies under control.
Main entities
-------------
Current section describes *Dependency Injector* main entities and their
interaction with each other.
.. image:: /images/internals.png
:width: 100%
:align: center
There are 3 main entities:
- Providers. Providers are strategies of accesing objects. For example,
:py:class:`dependency_injector.providers.Factory` creates new instance of
provided class every time it is called.
:py:class:`dependency_injector.providers.Singleton` creates provided
instance once and returns it on every next call. Providers could be
overridden by another providers. Base class is -
:py:class:`dependency_injector.providers.Provider`.
- Injections. Injections are instructions for making dependency injections
(there are several ways how they could be done). Injections are used mostly
by :py:class:`dependency_injector.providers.Factory` and
:py:class:`dependency_injector.providers.Singleton` providers, but
these are not only cases. Base class is -
:py:class:`dependency_injector.injections.Injection`.
- Catalogs. Catalogs are collections of providers. They are used for grouping
of providers by some principles. Base class is -
:py:class:`dependency_injector.catalogs.DeclarativeCatalog`.

View File

@ -0,0 +1,130 @@
What is Dependency Injection?
-----------------------------
Definition
~~~~~~~~~~
Wikipedia provides quite good definitions of Dependency Injection Pattern
and related principles:
.. glossary::
`Dependency Injection`_
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
inversion principle. The client delegates to external code (the
injector) the responsibility of providing its dependencies. The client
is not allowed to call the injector code. It is the injecting code
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`_
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
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
principle, which concerns itself with decoupling dependencies between
high-level and low-level layers through shared abstractions.
`Dependency Inversion`_
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.
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
object-oriented design, dictating that both high- and low-level
objects must depend on the same abstraction.
Example
~~~~~~~
Let's go through the code of ``example.py``:
.. literalinclude:: ../../../examples/di_demo/example.py
:language: python
At some point, things defined above mean, that the code from ``example.py``,
could look different, like in ``ioc_example.py``:
.. literalinclude:: ../../../examples/di_demo/ioc_example.py
:language: python
Also the code from ``ioc_example.py`` could be powered by dependency
injection framework, like in ``di_example.py``:
.. literalinclude:: ../../../examples/di_demo/di_example.py
:language: python
.. note::
``Components`` from ``di_example.py`` is an IoC container. It contains a
collection of component providers that could be injected into each other.
Assuming this, ``Components`` could be one and the only place, where
application's structure is being managed on the high level.
Best explanation, ever
~~~~~~~~~~~~~~~~~~~~~~
Some times ago `user198313`_ posted awesome `question`_ about dependency
injection on `StackOverflow`_:
.. note::
How to explain dependency injection to a 5-year-old?
And `John Munsch`_ provided absolutely Great answer:
.. note::
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
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.
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
down to eat.
.. _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/
.. _question: http://stackoverflow.com/questions/1638919/how-to-explain-dependency-injection-to-a-5-year-old/1639186
.. _user198313: http://stackoverflow.com/users/198313/user198313
.. _John Munsch: http://stackoverflow.com/users/31899/john-munsch

View File

@ -0,0 +1,20 @@
"""The Code, powered by Dependency Injector."""
from dependency_injector import catalogs, providers
from ioc_example import Service, Client
class Components(catalogs.DeclarativeCatalog):
"""Components catalog."""
service = providers.Factory(Service)
""":type: providers.Factory -> Service"""
client = providers.Factory(Client,
service=service)
""":type: providers.Factory -> Client"""
if __name__ == '__main__':
# Application creates Client's instance using its provider
client = Components.client() # equivalent of Client(service=Service())

View File

@ -0,0 +1,18 @@
"""The Code."""
class Service(object):
"""Some "Service"."""
class Client(object):
"""Some "Client" that uses "Service"."""
def __init__(self):
"""Initializer."""
self.service = Service() # Service instance is created inside Client
if __name__ == '__main__':
# Application just creates Client's instance
client = Client()

View File

@ -0,0 +1,21 @@
"""The Code, that follows Inversion of Control principle."""
class Service(object):
"""Some "Service"."""
class Client(object):
"""Some "Client" that uses "Service"."""
def __init__(self, service): # Service instance is injected in Client
"""Initializer."""
self.service = service
if __name__ == '__main__':
# Application creates Service instance
service = Service()
# and inject Service instance into the Client
client = Client(service)