Describe some antipatterns

This commit is contained in:
Jakub Stasiak 2015-02-15 17:21:36 +01:00
parent a0127c0713
commit 7496c1d2dd
2 changed files with 114 additions and 0 deletions

View File

@ -164,3 +164,91 @@ Here's the output of the application::
Sleeping...
Sleeping...
(...)
Injecting Injector and abusing Injector.get
```````````````````````````````````````````
Sometimes code like this is written:
.. code-block:: python
class A(object):
pass
class B(object):
pass
class C(object):
@inject(injector=Injector)
def __init__(self, injector):
self.a = injector.get(A)
self.b = injector.get(B)
It is advised to use the following pattern instead:
.. code-block:: python
class A(object):
pass
class B(object):
pass
class C(object):
@inject(a=A, b=B)
def __init__(self, a, b)
self.a = a
self.b = b
The second form has the benefits of:
* expressing clearly what the dependencies of ``C`` are
* making testing of the ``C`` class easier - you can provide the dependencies
(whether they are mocks or not) directly, instead of having to mock
:class:`Injector` and make the mock handle :meth:`Injector.get` calls
* following the common practice and being easier to understand
Injecting dependencies only to pass them somewhere else
```````````````````````````````````````````````````````
A pattern similar to the one below can emerge:
.. code-block:: python
class A(object):
pass
class B(object):
def __init__(self, a):
self.a = a
class C(object):
@inject(a=A)
def __init__(self, a):
self.b = B(a)
Class ``C`` in this example has the responsibility of gathering dependencies of
class ``B`` and constructing an object of type ``B``, there may be a valid reason
for it but in general it defeats the purpose of using ``Injector`` and should
be avoided.
The appropriate pattern is:
.. code-block:: python
class A(object):
pass
class B(object):
@inject(a=A)
def __init__(self, a):
self.a = a
class C(object):
@inject(b=B)
def __init__(self, b):
self.b = b

View File

@ -653,6 +653,32 @@ class Injector(object):
def get(self, interface, scope=None):
"""Get an instance of the given interface.
.. note::
Although this method is part of :class:`Injector`'s public interface
it's meant to be used in limited set of circumstances.
For example, to create some kind of root object (application object)
of your application (note that only one `get` call is needed,
inside the `Application` class and any of its dependencies
:func:`inject` can and should be used):
.. code-block:: python
class Application(object):
@inject(dep1=Dep1, dep2=dep2)
def __init__(self, dep1, dep2):
self.dep1 = dep1
self.dep2 = dep2
def run(self):
self.dep1.something()
injector = Injector(configuration)
application = injector.get(Application)
application.run()
:param interface: Interface whose implementation we want.
:param scope: Class of the Scope in which to resolve.
:returns: An implementation of interface.