Dependency Injection is a broad technique that can be implemented in many different ways. In this article, I will look at Dependency Injection and implement frequently used patterns.
1. Definition of Dependency Injection.
Dependency Injection is a technique that allows a class to depend on an object instead of attending the class to create those objects.
A class will depend on an interface and will implement outside that class. There are 3 things worth noting here:
- Injector: an instance of dependency and loosen it with a client.
- Dependency: is an object that requires the client to operate correctly.
- Client: An object where dependencies are made
Try it out with Swift in the next sections.
2. Client patterns.
There are 4 ways clients can receive dependencies:
- Initializer
- Property (property)
- Interface
- Ambient context (environment)
2.1 Initializer Injection.
- Description: Create dependencies through initialization.
- Usually used when the number of dependencies is small or the object is immutable.
- Sample code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <span class="token keyword">protocol</span> <span class="token builtin">Dependency</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">struct</span> <span class="token builtin">DependencyImplementation</span> <span class="token punctuation">:</span> <span class="token builtin">Dependency</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Does something</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Client</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> dependency <span class="token punctuation">:</span> <span class="token builtin">Dependency</span> <span class="token keyword">init</span> <span class="token punctuation">(</span> dependency <span class="token punctuation">:</span> <span class="token builtin">Dependency</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> dependency <span class="token operator">=</span> dependency <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> dependency <span class="token punctuation">.</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">let</span> client <span class="token operator">=</span> <span class="token function">Client</span> <span class="token punctuation">(</span> dependency <span class="token punctuation">:</span> <span class="token function">DependencyImplementation</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> client <span class="token punctuation">.</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> |
- Advantages:
- High packing properties
- Ensure the client is always valid
- Defect:
- Dependency cannot be changed later.
- Becomes bloated with more than 3 dependencies
2.2 Property Injection.
- Description: Create dependencies via properties.
- Usually you need to change dependency after initialization. For example Viewcontroller and NSManagedObject
- Sample code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <span class="token keyword">protocol</span> <span class="token builtin">Dependency</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">struct</span> <span class="token builtin">DependencyImplementation</span> <span class="token punctuation">:</span> <span class="token builtin">Dependency</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Does something</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Client</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> dependency <span class="token punctuation">:</span> <span class="token builtin">Dependency</span> <span class="token operator">!</span> <span class="token keyword">func</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> dependency <span class="token punctuation">.</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">let</span> client <span class="token operator">=</span> <span class="token function">Client</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> client <span class="token punctuation">.</span> dependency <span class="token operator">=</span> <span class="token function">DependencyImplementation</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> client <span class="token punctuation">.</span> <span class="token function">foo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> |
- Advantages:
- Allow to fix dependency after initialization
- The code is easier to read
- Defect:
- The client will fail if some dependencies are missing
- Weak packaging
- Optional attribute must be used
2.3 Interface Injection
- Description: implemented via setter method or parameter
- Usually used when multiple clients need to be handled by an injector. Allows injector to perform jobs on the client
- Sample code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | <span class="token keyword">protocol</span> <span class="token builtin">Dependency</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">protocol</span> <span class="token builtin">HasDependency</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">setDependency</span> <span class="token punctuation">(</span> <span class="token number">_</span> dependency <span class="token punctuation">:</span> <span class="token builtin">Dependency</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">protocol</span> <span class="token builtin">DoesSomething</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> <span class="token function">doSomething</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Client</span> <span class="token punctuation">:</span> <span class="token builtin">HasDependency</span> <span class="token punctuation">,</span> <span class="token builtin">DoesSomething</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">var</span> dependency <span class="token punctuation">:</span> <span class="token builtin">Dependency</span> <span class="token operator">!</span> <span class="token keyword">func</span> <span class="token function">setDependency</span> <span class="token punctuation">(</span> <span class="token number">_</span> dependency <span class="token punctuation">:</span> <span class="token builtin">Dependency</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> dependency <span class="token operator">=</span> dependency <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">doSomething</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Does something with a dependency</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Injector</span> <span class="token punctuation">{</span> <span class="token keyword">typealias</span> <span class="token builtin">Client</span> <span class="token operator">=</span> <span class="token builtin">HasDependency</span> <span class="token operator">&</span> <span class="token builtin">DoesSomething</span> <span class="token keyword">private</span> <span class="token keyword">var</span> clients <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token builtin">Client</span> <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span> <span class="token keyword">func</span> <span class="token function">inject</span> <span class="token punctuation">(</span> <span class="token number">_</span> client <span class="token punctuation">:</span> <span class="token builtin">Client</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> clients <span class="token punctuation">.</span> <span class="token function">append</span> <span class="token punctuation">(</span> client <span class="token punctuation">)</span> client <span class="token punctuation">.</span> <span class="token function">setDependency</span> <span class="token punctuation">(</span> <span class="token function">SomeDependency</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token comment">// Dependency applies its policies over clients</span> client <span class="token punctuation">.</span> <span class="token function">doSomething</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token comment">// Switch dependencies under certain conditions</span> <span class="token keyword">func</span> <span class="token function">switchToAnotherDependency</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> clients <span class="token punctuation">.</span> forEach <span class="token punctuation">{</span> $ <span class="token number">0</span> <span class="token punctuation">.</span> <span class="token function">setDependency</span> <span class="token punctuation">(</span> <span class="token function">AnotherDependency</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">SomeDependency</span> <span class="token punctuation">:</span> <span class="token builtin">Dependency</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">AnotherDependency</span> <span class="token punctuation">:</span> <span class="token builtin">Dependency</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> |
In the example above, Injector
handles any client that has HasDependency
and DoesSomething
protocol. And Injector
did doSomething()
.
- Advantages:
- Lets set the dependency later.
- Allow injector to apply some client jobs.
- Injector can handle any client that satisfies the protocol.
- Defect:
- The client becomes dependent on itself which complicates flow.
2.4 Ambient Context.
- Description: The dependency is global through the protocol. This allows substitute implementation if needed.
- Often used when creating a global access dependency.
- Sample code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <span class="token keyword">protocol</span> <span class="token builtin">DateTimeProvider</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> now <span class="token punctuation">:</span> <span class="token builtin">Date</span> <span class="token punctuation">{</span> <span class="token keyword">get</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">struct</span> <span class="token builtin">SystemDateTimeProvider</span> <span class="token punctuation">:</span> <span class="token builtin">DateTimeProvider</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> now <span class="token punctuation">:</span> <span class="token builtin">Date</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">Date</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">DateTime</span> <span class="token punctuation">{</span> <span class="token keyword">static</span> <span class="token keyword">var</span> provider <span class="token punctuation">:</span> <span class="token builtin">DateTimeProvider</span> <span class="token operator">=</span> <span class="token function">SystemDateTimeProvider</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">static</span> <span class="token keyword">var</span> now <span class="token punctuation">:</span> <span class="token builtin">Date</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> provider <span class="token punctuation">.</span> now <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
- Advantages:
- Dependency is global and reduces the complexity for clients that use it.
- Defect:
- Weak packaging
- Requires thread safety
- Heiuer needs to be used with any client that needs manual testing.
3. Dependency Injection Patterns
There are 3 commonly used patterns:
- Factory
- Service Locator
- Dependency injection Container
3.1 Factory
The factory always acts as an injector and connects the client with that dependency. The purpose of the factory is to separate dependencies from the clients.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <span class="token keyword">protocol</span> <span class="token builtin">Client</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">enum</span> <span class="token builtin">ClientFactory</span> <span class="token punctuation">{</span> <span class="token keyword">static</span> <span class="token keyword">func</span> <span class="token function">make</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token builtin">Client</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">ClientImplementation</span> <span class="token punctuation">(</span> dependency <span class="token punctuation">:</span> <span class="token function">DependencyImplementation</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">ClientImplementation</span> <span class="token punctuation">:</span> <span class="token builtin">Client</span> <span class="token punctuation">{</span> <span class="token keyword">init</span> <span class="token punctuation">(</span> dependency <span class="token punctuation">:</span> <span class="token builtin">Dependency</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">protocol</span> <span class="token builtin">Dependency</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">struct</span> <span class="token builtin">DependencyImplementation</span> <span class="token punctuation">:</span> <span class="token builtin">Dependency</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> |
3.2 Dependency Injection Container
A Container contains several types of abstractions serving a range of functions:
- Create dependencies with the client
- Create object
- Managing the object’s life cycle
- Apply Container service separately to object
Containers are especially useful when you need to manage multiple clients with multiple dependencies.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | <span class="token keyword">final</span> <span class="token keyword">class</span> <span class="token class-name">Assembly</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">let</span> view <span class="token operator">=</span> <span class="token function">View</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">private</span> <span class="token keyword">let</span> presenter <span class="token operator">=</span> <span class="token function">Presenter</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">private</span> <span class="token keyword">let</span> interactor <span class="token operator">=</span> <span class="token function">Interactor</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">private</span> <span class="token keyword">let</span> router <span class="token operator">=</span> <span class="token function">Router</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">var</span> input <span class="token punctuation">:</span> <span class="token builtin">ModuleInput</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> presenter <span class="token punctuation">}</span> <span class="token keyword">weak</span> <span class="token keyword">var</span> output <span class="token punctuation">:</span> <span class="token builtin">ModuleOutput</span> <span class="token operator">?</span> <span class="token punctuation">{</span> <span class="token keyword">didSet</span> <span class="token punctuation">{</span> presenter <span class="token punctuation">.</span> output <span class="token operator">=</span> output <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">init</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> view <span class="token punctuation">.</span> output <span class="token operator">=</span> presenter interactor <span class="token punctuation">.</span> output <span class="token operator">=</span> presenter router <span class="token punctuation">.</span> output <span class="token operator">=</span> presenter presenter <span class="token punctuation">.</span> view <span class="token operator">=</span> view presenter <span class="token punctuation">.</span> interactor <span class="token operator">=</span> interactor presenter <span class="token punctuation">.</span> router <span class="token operator">=</span> router <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">View</span> <span class="token punctuation">{</span> <span class="token keyword">weak</span> <span class="token keyword">var</span> output <span class="token punctuation">:</span> <span class="token builtin">ViewOutput</span> <span class="token operator">!</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Presenter</span> <span class="token punctuation">{</span> <span class="token keyword">weak</span> <span class="token keyword">var</span> view <span class="token punctuation">:</span> <span class="token builtin">ViewInput</span> <span class="token operator">!</span> <span class="token keyword">weak</span> <span class="token keyword">var</span> interactor <span class="token punctuation">:</span> <span class="token builtin">InteractorInput</span> <span class="token operator">!</span> <span class="token keyword">weak</span> <span class="token keyword">var</span> router <span class="token punctuation">:</span> <span class="token builtin">RouterInput</span> <span class="token operator">!</span> <span class="token keyword">weak</span> <span class="token keyword">var</span> output <span class="token punctuation">:</span> <span class="token builtin">ModuleOutput</span> <span class="token operator">!</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Interactor</span> <span class="token punctuation">{</span> <span class="token keyword">weak</span> <span class="token keyword">var</span> output <span class="token punctuation">:</span> <span class="token builtin">InteractorOutput</span> <span class="token operator">!</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Router</span> <span class="token punctuation">{</span> <span class="token keyword">weak</span> <span class="token keyword">var</span> output <span class="token punctuation">:</span> <span class="token builtin">RouterOutput</span> <span class="token operator">!</span> <span class="token punctuation">}</span> |
3.3 Service Locator.
It is used instead of directly creating dependencies, we need to use the Locator object – responsible for checking each dependency. Locator provides a way to register dependency and manage its life cycle. It does not create dependencies
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <span class="token keyword">protocol</span> <span class="token builtin">Locator</span> <span class="token punctuation">{</span> <span class="token keyword">func</span> resolve <span class="token operator"><</span> T <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> T <span class="token operator">?</span> <span class="token punctuation">}</span> <span class="token keyword">final</span> <span class="token keyword">class</span> <span class="token class-name">LocatorImpl</span> <span class="token punctuation">:</span> <span class="token builtin">Locator</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">var</span> services <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token builtin">ObjectIdentifier</span> <span class="token punctuation">:</span> <span class="token builtin">Any</span> <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">:</span> <span class="token punctuation">]</span> <span class="token keyword">func</span> register <span class="token operator"><</span> T <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token number">_</span> service <span class="token punctuation">:</span> T <span class="token punctuation">)</span> <span class="token punctuation">{</span> services <span class="token punctuation">[</span> <span class="token function">key</span> <span class="token punctuation">(</span> <span class="token keyword">for</span> <span class="token punctuation">:</span> T <span class="token punctuation">.</span> <span class="token keyword">self</span> <span class="token punctuation">)</span> <span class="token punctuation">]</span> <span class="token operator">=</span> service <span class="token punctuation">}</span> <span class="token keyword">func</span> resolve <span class="token operator"><</span> T <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> T <span class="token operator">?</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> services <span class="token punctuation">[</span> <span class="token function">key</span> <span class="token punctuation">(</span> <span class="token keyword">for</span> <span class="token punctuation">:</span> T <span class="token punctuation">.</span> <span class="token keyword">self</span> <span class="token punctuation">)</span> <span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token operator">?</span> T <span class="token punctuation">}</span> <span class="token keyword">private</span> <span class="token keyword">func</span> key <span class="token operator"><</span> T <span class="token operator">></span> <span class="token punctuation">(</span> <span class="token keyword">for</span> type <span class="token punctuation">:</span> T <span class="token punctuation">.</span> <span class="token keyword">Type</span> <span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token operator">></span> <span class="token builtin">ObjectIdentifier</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">ObjectIdentifier</span> <span class="token punctuation">(</span> T <span class="token punctuation">.</span> <span class="token keyword">self</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Client</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">let</span> locator <span class="token punctuation">:</span> <span class="token builtin">Locator</span> <span class="token keyword">init</span> <span class="token punctuation">(</span> locator <span class="token punctuation">:</span> <span class="token builtin">Locator</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">self</span> <span class="token punctuation">.</span> locator <span class="token operator">=</span> locator <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">doSomething</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">guard</span> <span class="token keyword">let</span> service <span class="token punctuation">:</span> <span class="token builtin">Service</span> <span class="token operator">=</span> locator <span class="token punctuation">.</span> <span class="token function">resolve</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">}</span> <span class="token comment">// do something with service</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">class</span> <span class="token class-name">Service</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> |
4. Summary.
Dependency injection is a useful technique, which makes code cleaner and easier to maintain. It allows separate initialization of objects from its use and minimizes connection between components.