From 08b00d71d4a01ad3aca03fd4098a02d58ecae0e1 Mon Sep 17 00:00:00 2001 From: Roman Mogilatov Date: Tue, 29 Mar 2016 20:17:12 +0300 Subject: [PATCH] Update introduction docs --- docs/index.rst | 8 +- docs/main/introduction.rst | 93 ---------------- docs/main/introduction/di_in_python.rst | 10 ++ docs/main/introduction/index.rst | 12 +++ docs/main/introduction/introduction_.rst | 45 ++++++++ docs/main/introduction/what_is_di.rst | 130 +++++++++++++++++++++++ examples/di_demo/di_example.py | 20 ++++ examples/di_demo/example.py | 18 ++++ examples/di_demo/ioc_example.py | 21 ++++ 9 files changed, 258 insertions(+), 99 deletions(-) delete mode 100644 docs/main/introduction.rst create mode 100644 docs/main/introduction/di_in_python.rst create mode 100644 docs/main/introduction/index.rst create mode 100644 docs/main/introduction/introduction_.rst create mode 100644 docs/main/introduction/what_is_di.rst create mode 100644 examples/di_demo/di_example.py create mode 100644 examples/di_demo/example.py create mode 100644 examples/di_demo/ioc_example.py diff --git a/docs/index.rst b/docs/index.rst index 66828cae..ab3f517a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -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 - diff --git a/docs/main/introduction.rst b/docs/main/introduction.rst deleted file mode 100644 index ab0f1a57..00000000 --- a/docs/main/introduction.rst +++ /dev/null @@ -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 diff --git a/docs/main/introduction/di_in_python.rst b/docs/main/introduction/di_in_python.rst new file mode 100644 index 00000000..7e111676 --- /dev/null +++ b/docs/main/introduction/di_in_python.rst @@ -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. diff --git a/docs/main/introduction/index.rst b/docs/main/introduction/index.rst new file mode 100644 index 00000000..fb1773d1 --- /dev/null +++ b/docs/main/introduction/index.rst @@ -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 diff --git a/docs/main/introduction/introduction_.rst b/docs/main/introduction/introduction_.rst new file mode 100644 index 00000000..74dc00d8 --- /dev/null +++ b/docs/main/introduction/introduction_.rst @@ -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`. diff --git a/docs/main/introduction/what_is_di.rst b/docs/main/introduction/what_is_di.rst new file mode 100644 index 00000000..ae0fda33 --- /dev/null +++ b/docs/main/introduction/what_is_di.rst @@ -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 diff --git a/examples/di_demo/di_example.py b/examples/di_demo/di_example.py new file mode 100644 index 00000000..43bb406d --- /dev/null +++ b/examples/di_demo/di_example.py @@ -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()) diff --git a/examples/di_demo/example.py b/examples/di_demo/example.py new file mode 100644 index 00000000..3d498d9c --- /dev/null +++ b/examples/di_demo/example.py @@ -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() diff --git a/examples/di_demo/ioc_example.py b/examples/di_demo/ioc_example.py new file mode 100644 index 00000000..08d2fa88 --- /dev/null +++ b/examples/di_demo/ioc_example.py @@ -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)