Dependency Injection and Dependency Lookup are two basic ways to provide Inversion of Control.
Inversion of Control is a principle which recommends moving unwanted responsibilities out of a class and letting the class focus on core responsibilities, hence providing loose coupling.
Dependency Injection is where the dependency is injected from outside the class, and class is not worried about details.
Initializer injection is a pattern for passing dependencies to a dependent instance by its initializers. Initializer injection is appropriate if the dependent instance cannot work without the dependencies.
protocol Name {
var firstName: String { get }
var lastName: String { get }
}
class Person {
let name: Name
init(name: Name) {
self.name = name
}
}
Objective-C Sample
@protocol Name
- (NSString *)firstName;
- (NSString *)lastName;
@end
@interface Person: NSObject
@property (nonatomic, strong) id<Name> name;
- (instancetype)initWithName:(id<Name>)name;
@end
In ZIKRouter, use module config protocol to make Initializer Injection:
protocol PersonConfig {
func constructWithName(_ name: Name) -> Void
}
class PersonConfiguration: ZIKPerformRouteConfiguration, PersonConfig {
var name: Name?
func constructWithName(name: Name) {
self.name = name
}
}
class PersonRouter: ZIKServiceRouter<Person, PersonConfiguration> {
...
override func destination(with configuration: PersonConfiguration) -> Person? {
guard let name = configuration.name else {
return nil
}
return Person(name: name)
}
}
The performer must call construct
method in config protocol:
let name: Name = ...
let person = Router.makeDestination(to: RoutableServiceModule<PersonConfig>(), preparation: { moduleConfig in
moduleConfig.constructWithName(name)
})
Objective-C Sample
@protocol PersonConfig: ZIKServiceModuleRoutable
- (void)constructWithName:(id<Name>)name;
@end
@interface PersonConfiguration: ZIKPerformConfiguration <PersonConfig>
@property (nonatomic, strong) id<Name> name;
- (void)constructWithName:(id<Name>)name;
@end
@implementation PersonConfiguration
- (void)constructWithName:(id<Name>)name {
self.name = name;
}
@end
@interface PersonRouter: ZIKServiceRouter<Person *, PersonConfiguration *>
@end
@implementation PersonRouter
- (nullable Person *)destinationWithConfiguration:(PersonConfiguration *)configuration {
id<Name> name = configuration.name;
if (name == nil) {
return nil;
}
return [[Person alloc] initWithName:name];
}
@end
id<Name> name = ...
Person *person = [ZIKRouterToServiceModule(PersonConfig)
makeDestinationWithConfiguring:^(ZIKPerformRouteConfiguration<PersonConfig> * _Nonnull config) {
[config constructWithName:name];
}];
Property injection is a pattern to pass a dependency to a dependent instance via a setter property.
Method injection is a similar pattern to property injection, but it uses a method to pass dependencies to a dependent instance.
Property injection and method injection are appropriate if the dependency is optional to the dependent instance.
protocol PersonType {
var wife: Person? { get set }
func addChild(_ child: Person) -> Void
}
protocol Child {
var parent: Person { get }
}
class Person: PersonType {
var wife: Person? = nil
var childs: Set<Child> = []
func addChild(_ child: Child) {
childs.insert(child)
}
}
Objective-C Sample
@protocol PersonType: ZIKServiceRoutable
@property (nonatomic, strong, nullable) Person *wife;
- (void)addChild:(Person *)child;
@end
@protocol Child
@property (nonatomic, strong) Person *parent;
@end
@interface Person: NSObject <PersonType>
@property (nonatomic, strong, nullable) Person *wife;
@property (nonatomic, strong) NSSet<id<Child>> childs;
@end
You can inject default dependencies in router:
class PersonRouter: ZIKServiceRouter<Person, ZIKPerformRouteConfiguration> {
...
override func destination(with configuration: ZIKPerformRouteConfiguration) -> Person? {
let person = Person()
//Set default value
//person.wife = ...
return person
}
}
The performer can inject dependencies at runtime:
let wife: Person = ...
let child: Child = ...
let person = Router.makeDestination(to: RoutableService<PersonType>(), preparation: { destination in
destination.wife = wife
destination.addChild(child)
})
Objective-C Sample
@interface PersonRouter: ZIKServiceRouter<Person *, ZIKPerformRouteConfiguration *>
@end
@implementation PersonRouter
- (nullable Person *)destinationWithConfiguration:(ZIKPerformRouteConfiguration *)configuration {
Person *person = [Person new];
///[person addChild:...];
return person;
}
@end
Person *wife = ...
Child *child = ...
Person *person = [ZIKRouterToService(PersonType)
makeDestinationWithPreparation:^(id<PersonType> destination) {
destination.wife = wife;
[destination addChild:child];
}];
Dependency lookup is where class itself looks up for dependency.
Getting router with protocol is Dependency Lookup.