Skip to content

Commit

Permalink
Describe some antipatterns
Browse files Browse the repository at this point in the history
  • Loading branch information
jstasiak committed Feb 15, 2015
1 parent a0127c0 commit 7496c1d
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 0 deletions.
88 changes: 88 additions & 0 deletions docs/practices.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
26 changes: 26 additions & 0 deletions injector.py
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,32 @@ def _log_prefix(self):
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.
Expand Down

0 comments on commit 7496c1d

Please sign in to comment.