https://yalantis.com/blog/dependency-injection-di-service-locator/
http://stackoverflow.com/questions/3058/what-is-inversion-of-control
In case the link does not work, the article can be downloaded and opened from your local machine here
The Problem
follow along with the code in Dependency Injection
Say you have a MovieLister object that has a method moviesDirectedByDirector. It uses a class called TextFileMovieFinder in order to call methods on it and get return values for the results.
This is the simplest solution, but yet, it creates tight-coupling because the method full depends on the exact implementation of TestFileMovieFinder.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
- (NSArray *)moviesDirectedByDirector:(NSString *)director { // 1) // What is Finder and where it came from? // // We need to have Finder inside the method to get the list of the movies. // How can we get the Finder inside the method? // // The simplest and wrong solution is to create the required object inside the method. // // In this case the method (moviesDirectedByDirector) fully depends on the exact implementation // of the exact class TestFileMovieFinder. // TextFileMovieFinder * finder = [[TextFileMovieFinder alloc] initWithTextFileName:@"movies.txt"]; NSArray *allMovies = [finder findAllMovies]; NSMutableArray *result = [NSMutableArray array]; for (Movie * movie in allMovies) { if ([movie.director isEqualToString:director]) { [result addObject:movie]; } } return result; |
Implementing a protocol and using delegate for loose coupling
In the MovieLister, our job is to list. We just need to connect it to a movie finder that gives us the results.
We do this by having a delegate that points to any object that conforms to MovieFinder.
That way, we don’t care what that object does, just give us the result via the protocol MovieFinder’s
1 |
- (NSArray *)findAllMovies; |
1 2 3 4 5 6 7 8 9 10 11 |
@interface MovieLister : NSObject { } //Whatever object that implements the MovieFinder protocol //hold onto the movie finder object @property (nonatomic, strong) id <MovieFinder> finder; @end |
..and you use it like so:
1 2 3 4 5 6 7 8 9 10 11 12 |
- (NSArray *)moviesDirectedByDirector:(NSString *)director { NSArray * allMovies = [self.finder findAllMovies]; NSMutableArray *result = [NSMutableArray array]; for (Movie * movie in allMovies) { if ([movie.director isEqualToString:director]) { [result addObject:movie]; } } return result; } |
Now, let’s update our TextFileMovieFinder object so that it conforms to the MovieFinder protocol.
TextFileMovieFinder.h
1 2 3 4 5 6 7 8 9 |
@protocol MovieFinder <NSObject> - (NSArray *)findAllMovies; @end @interface TextFileMovieFinder : NSObject <MovieFinder> { } |
TextFileMovieFinder.m
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 |
#import "TextFileMovieFinder.h" @interface TextFileMovieFinder () { } @property(nonatomic, strong) NSString * fileName; @end @implementation TextFileMovieFinder - (id)initWithTextFileName:(NSString *)textFileName { if(self=[super init]) { self.fileName = textFileName; } return self; } #pragma mark MovieFinder - (NSArray *)findAllMovies { return [NSArray arrayWithObjects: @"Saving Private Ryan", @"Old Boy", @"The Karate Kid", @"Matrix", nil]; } |
But how do we connect them?
1) Constructor Injection
1 2 |
TextFileMovieFinder * finder = [[TextFileMovieFinder alloc] initWithTextFileName:@"movies.txt"]; MovieLister * lister = [[MovieLister alloc] initWithMovieFinder:finder]; |
Constructor injection is perfect for “obligatory” dependencies, without which the class can’t implement its task. This way we locate all the dependencies in one place – in the constructor. When the class expands and starts using additional services, they will appear in the constructor as parameters which cannot be left unnoticed. So when another parameter is added to the constructor and the number of all the parameters becomes higher than 4, you need to think about the architecture of your class.
2) Setter Injection
1 2 3 4 |
TextFileMovieFinder * finder = [[TextFileMovieFinder alloc] initWithTextFileName:@"movies.txt"]; MovieLister * lister = [[MovieLister alloc] init]; [lister setFinder:finder]; |
Setter injection (property injection) fits “optional” dependencies, those which have a reasonable implementation known to the class by default. At the same time there has to be a possibility to change the dependency while the class is working and without any negative consequences.
Delegates
http://stackoverflow.com/questions/7052926/what-is-the-purpose-of-an-ios-delegate
The advantage of the delegate design pattern is loose coupling. It enables class A (the delegate) to depend on class B (the delegating class) without class B having to have any knowledge of class A. This ensures that the dependency relationship is one-way only, rather than being circular.
It also forms the foundation (lower case “f”) of Apple’s frameworks because it allows them to invoke your code as appropriate when functionality specific to your application is required. For example, responding to a button tap or telling a table view how many sections there should be.