Describe some antipatterns
This commit is contained in:
parent
a0127c0713
commit
7496c1d2dd
|
@ -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
injector.py
26
injector.py
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue