依赖注入浅析

一、名词解析。

维基百科上的解释是依赖注入(Dependency Injection,简称DI)是实现控制反转(Inversion of Control,缩写为IoC)的一种用以降低代码耦合度的一种设计模式。

二、进入主题。

请先看下面的示例有什么问题没有

@interface Service : NSObject
- (void)doSomeThing;
@end

@implementation Service
- (void)doSomeThing {
    // TODO:
}
@end

@interface Client : NSObject {
    Service *_service;
}
- (void)doTheWork;
@end

@implementation Client

- (instancetype)init {
    self = [super init];
    if (self) {
        _service = [Service new];
    }
    return self;
}

- (void)doTheWork {
    NSLog(@"I'm do the work, and ask service do some subwork");
    [_service doSomeThing];
}

@end

咋一看,这样的代码看起来好像也没有什么问题:Client有一个Service成员,Client直接New一个Service成员,然后在工作时将一部分转包给Service去做。但是,在这种情况下,其实Client和Service就有一种hard-coded的依赖。

依赖注入,就是让Client不去显示的通过构造函数(objc's all & init or new)初始化成员变量的技术。

 

依赖注入有三种方式:

1. 构造函数注入:通过构造函数去提供所依赖的对象 (objc的init)

@implementation Client

- (instancetype)initWithService:(Service *)sevice {
    self = [super init];
    if (self) {
        _service = sevice;
    }
    return self;
}

@end

 

2. setter注入

@implementation Client

- (void)setService:(Service *)service {
    _service = service;
}

@end

  

3. 接口注入

@protocol ServiceProtocol <NSObject>

- (void)doSomeThing;

@end
@interface Service : NSObject <ServiceProtocol>
@end

@implementation Service
- (void)doSomeThing {
    // TODO:
}
@end

@interface Client : NSObject {
    id<ServiceProtocol> _service;
}
- (void)doTheWork;
@end

@implementation Client

- (void)setService:(id<ServiceProtocol>)service {
    _service = service;
}

@end

  

我们可以看到,其实构造函数注入和setter注入也还是都依赖于具体的service类开。所以第三种接口注入其实才是比较好的方式。当然还应该加上一些对边界条件的检测。把三种注入方法接合起来。

@implementation Client

- (void)setService:(id<ServiceProtocol>)service {
    _service = service;
}

- (instancetype)initWithService:(id<ServiceProtocol>)sevice {
    self = [super init];
    if (self) {
        _service = sevice;
    }
    return self;
}

- (BOOL)validateState {
    if (!_service) {
        NSLog(@"Service cannot be nil");
        // TODO: maybe throw a exception
        return NO;
    }
    return YES;
}

- (BOOL)doTheWork {
    BOOL done = NO;
    if ([self validateState]) {
        NSLog(@"I'm do the work, and ask service do some subwork");
        [_service doSomeThing];
        done = YES;
    }
    return done;
}

@end

  

有的人已经看不下去了:不就经常TM说的面向接口编程吗?yes, you are right.

还有人要说了,我们用第三方的库,用系统的库,有太多地方都是直接依赖的了,而且如果所有地方都要这样不直接依赖,那还不搞死人?

 

是的,面向对象也好,面向接口也好,都是有一个度的。做人做事何尝又不是呢。面向对象与面向过程不是互斥的。无论你怎么抽象、怎么面积对象,到具体的方法逻辑里面那一定是面向过程的。

一样的,通过setter注入也好,接口注入也好,最后真正决定要使用哪一个实现了service protocol的类型的时候,总得有一个地方去初始化吧?那么好了,在那个地方一定就是和这个具体的Service类型强依赖的。我们要做的只是把一些今后可能会变的地方,一些粒度大一些的模块这样实现便好。

 

关于依赖注入的库:Typhoon(objc, swift) Swinject 

 

Reference: https://en.wikipedia.org/wiki/Dependency_injection

posted on 2015-08-22 12:00  课蜜黄蜂  阅读(486)  评论(0编辑  收藏  举报