I like to use composition and dependency injection, but when each entity starts injecting with several dependencies, it turns out some jumble.
The project is growing and you have to inject more and more dependencies into objects, refactor methods many times, Xcode doesn't really help with this, as we know.
But there is a more manageable way.
The article will be useful to those who have just started using DI or do not use it at all, and are not particularly familiar with IoC. The approach is applicable to other languages, but because the author writes in Swift, all examples will be on it.
Suppose there is an object that suddenly needs an ImageProvider
provider, we write something like this:
class FooCoordinator { let imageProvider: ImageProvider init(..., imageProvider: ImageProvider) ///... }
Quite simply and conveniently + this will allow you to change the provider in the tests.
As the code base grows, more and more dependencies appear, each of which causes:
for example, after several months, an object may have 3 dependencies:
class FooCoordinator { let imageProvider: ImageProvider let articleProvider: ArticleProvider let persistanceProvider: PersistanceProvider init(..., imageProvider: ImageProvider, articleProvider: ArticleProvider, persistanceProvider: PersistanceProvider) { self.imageProvider = imageProvider self.articleProvider = articleProvider self.persistanceProvider = persistanceProvider ///... } ///... }
Since usually there is more than one class in a project, the same pattern is repeated many times.
And we must not forget that it is necessary to store references to all these dependencies somewhere, for example, in AppController
or Flow Coordinator
.
Such an approach inevitably leads to pain. Pain can motivate the use of not quite the right decisions, for example, Singltons.
But we need simple and easy code support, with all the advantages of code injection.
We can use a mix of protocols (interfaces) to increase the readability and quality of service of the code.
Let's describe the basic protocol container for any dependency that we have:
protocol Has{Dependency} { var {dependency}: {Dependency} { get } }
Change {Dependency} to object name
For example, for ImageProvider
it will look like this:
protocol HasImageProvider { var imageProvider: ImageProvider { get } }
Swift allows composing from protocols using the &
operator, which means that our entities can now contain just one dependency storage:
class FooCoordinator { typealias Dependencies = HasImageProvider & HasArticleProvider let dependencies: Dependencies init(..., dependencies: Dependencies) }
Now in AppController
or Flow Coordinator
(Or whatever is used to create entities), you can hide all dependencies under one container in the form of a structure:
struct AppDependency: HasImageProvider, HasArticleProvider, HasPersistanceProvider { let imageProvider: ImageProvider let articleProvider: ArticlesProvider let persistanceProvider: PersistenceProvider }
And all the dependencies of the application will now be stored in a container that does not have any logic or something magical, just a structure.
This approach improves readability, and all dependencies are stored together, but more importantly, the configuration code is always the same, no matter which dependencies your object needs:
class FlowCoordinator { let dependencies: AppDependency func configureViewController(vc: ViewController) { vc.dependencies = dependencies } }
Each object describes only those dependencies that it really needs, and it will receive only them.
For example, if our FooCoordinator
needs an ImageProvider
, then it will ImageProvider
AppDependency
structure, and type checking in Swift will provide access only to ImageProvider
If you need more dependencies in the future, for example, PersistanceProvider
, then you just need to add it to our typealias
:
class FooCoordinator { typealias Dependencies = HasImageProvider & HasArticleProvider & HasPersistanceProvider }
That's all.
This approach has several advantages:
typealias
needs to be changed typealias
Source: https://habr.com/ru/post/326712/
All Articles