Skip to content

Commit 4ec3cce

Browse files
committed
Merge branch 'release/3.38.0' into master
2 parents 2cf5efa + d6dbef0 commit 4ec3cce

File tree

11 files changed

+231
-174
lines changed

11 files changed

+231
-174
lines changed

README.rst

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,25 @@ It helps implementing the dependency injection principle.
5757
What is dependency injection?
5858
-----------------------------
5959

60-
Dependency injection is a principle that helps to decrease coupling and increase cohesion. Your
61-
code becomes more flexible, clear and it is easier to test it.
60+
Dependency injection is a principle that helps to decrease coupling and increase cohesion.
61+
62+
What is coupling and cohesion?
63+
64+
Coupling and cohesion are about how tough the components are tied.
65+
66+
- **High coupling**. If the coupling is high it's like using a superglue or welding. No easy way
67+
to disassemble.
68+
- **High cohesion**. High cohesion is like using the screws. Very easy to disassemble and
69+
assemble back or assemble a different way. It is an alternative to high coupling.
70+
71+
When the cohesion is high the coupling is low.
72+
73+
High cohesion brings the flexibility. Your code becomes easier to change and test.
6274

6375
How to implement dependency injection?
6476
--------------------------------------
6577

66-
Objects do not create each other anymore. They provide a way to inject the needed dependencies
67-
instead.
78+
Objects do not create each other anymore. They provide a way to inject the dependencies instead.
6879

6980
Before:
7081

@@ -76,14 +87,14 @@ Before:
7687
class ApiClient:
7788
7889
def __init__(self):
79-
self.api_key = os.getenv('API_KEY')
80-
self.timeout = os.getenv('TIMEOUT')
90+
self.api_key = os.getenv('API_KEY') # <-- the dependency
91+
self.timeout = os.getenv('TIMEOUT') # <-- the dependency
8192
8293
8394
class Service:
8495
8596
def __init__(self):
86-
self.api_client = ApiClient()
97+
self.api_client = ApiClient() # <-- the dependency
8798
8899
89100
if __name__ == '__main__':
@@ -100,28 +111,36 @@ After:
100111
class ApiClient:
101112
102113
def __init__(self, api_key: str, timeout: int):
103-
self.api_key = api_key
104-
self.timeout = timeout
114+
self.api_key = api_key # <-- the dependency is injected
115+
self.timeout = timeout # <-- the dependency is injected
105116
106117
107118
class Service:
108119
109120
def __init__(self, api_client: ApiClient):
110-
self.api_client = api_client
121+
self.api_client = api_client # <-- the dependency is injected
111122
112123
113124
if __name__ == '__main__':
114125
service = Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT')))
115126
116127
117-
Flexibility comes with a price: now you need to assemble your objects like this
128+
``ApiClient`` is decoupled from knowing where the options come from. You can read a key and a
129+
timeout from a configuration file or even get them from a database.
130+
131+
``Service`` is decoupled from the ``ApiClient``. It does not create it anymore. You can provide a
132+
stub or other compatible object.
133+
134+
Flexibility comes with a price.
135+
136+
Now you need to assemble your objects like this
118137
``Service(ApiClient(os.getenv('API_KEY'), os.getenv('TIMEOUT')))``. The assembly code might get
119138
duplicated and it'll become harder to change the application structure.
120139

121140
What does Dependency Injector do?
122141
---------------------------------
123142

124-
``Dependency Injector`` helps you assemble the objects.
143+
``Dependency Injector`` helps to assemble the objects.
125144

126145
It provides you the container and the providers that help you describe objects assembly. When you
127146
need an object you get it from the container. The rest of the assembly work is done by the
@@ -170,8 +189,11 @@ framework:
170189
171190
Retrieving of the ``Service`` instance now is done like this ``container.service()``.
172191

173-
Also ``Dependency Injector`` provides a bonus in overriding any of the providers with the
174-
``.override()`` method:
192+
The responsibility of assembling the object is consolidated in the container. When you need to
193+
make a change you do it in one place.
194+
195+
When doing the testing you call the ``container.api_client.override()`` to replace the real API
196+
client with a mock:
175197

176198
.. code-block:: python
177199
@@ -180,7 +202,6 @@ Also ``Dependency Injector`` provides a bonus in overriding any of the providers
180202
181203
with container.api_client.override(mock.Mock()):
182204
service = container.service()
183-
assert isinstance(service.api_client, mock.Mock)
184205
185206
It helps in a testing. Also you can use it for configuring project for the different environments:
186207
replace an API client with a stub on the dev or stage.
@@ -217,7 +238,7 @@ Concept
217238
- Explicit is better than implicit (PEP20).
218239
- Do no magic to your code.
219240

220-
How does it different from the other frameworks?
241+
How is it different from the other frameworks?
221242

222243
- **No autowiring.** The framework does NOT do any autowiring / autoresolving of the dependencies. You need to specify everything explicitly. Because *"Explicit is better than implicit" (PEP20)*.
223244
- **Does not pollute your code.** Your application does NOT know and does NOT depend on the framework. No ``@inject`` decorators, annotations, patching or any other magic tricks.
8.16 KB
Loading

0 commit comments

Comments
 (0)