2015-05-12 17:19:41 574浏览
objection是一个轻量级的依赖注入框架,受Guice的启发,Google Wallet也是使用的该项目。「依赖注入」是面向对象编程的一种设计模式,用来减少代码之间的耦合度。通常基于接口来实现,也就是说不需要new一个对象,而是通过相关的控制器来获取对象。2013年最火的PHP框架laravel就是其中的典型。
假设有以下场景:ViewControllerA的view里有一个button,点击之后push到ViewControllerB,最简单的写法类似这样:
Ios代码
1. - (void)viewDidLoad {
2. [super viewDidLoad];
3.
4. UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
5. button.frame = CGRectMake(100, 100, 100, 30);
6. [button setTitle:@"PUSH" forState:UIControlStateNormal];
7. [button addTarget:self action:@selector(buttonTapped) forControlEvents:UIControlEventTouchUpInside];
8. [self.view addSubview:button];
9. }
10.
11. - (void)buttonTapped {
12. ViewControllerB *vc = [[ViewControllerB alloc] init];
13. [self.navigationController pushViewController:vc animated:YES];
14. }
复制代码
这样写的一个问题是,ViewControllerA需要import ViewControllerB,也就是对ViewControllerB产生了依赖。依赖的东西越多,维护起来就越麻烦,也容易出现循环依赖的问题,而objection正好可以处理这些问题。
下面我们来看下使用objection如何实现上面的需求。
首先,定义一个协议(protocol),然后通过objection来注册这个协议对应的Class,需要的时候,可以获取该协议对应的object。对于使用方无需关心到底使用的是哪个Class,反正该有的方法、属性都有了(在协议中指定)。这样就去除了对某个特定Class的依赖。也就是通常所说的「面向接口编程」。
Ios代码
1. // 获取默认的injector,这个injector已经注册过BViewControllerProtocol了 2. JSObjectionInjector *injector = [JSObjection defaultInjector]; 3. 4. // 获取BViewControllerProtocol对应的Object 5. UIViewController <BViewControllerProtocol> *vc = [injector getObject:@protocol(BViewControllerProtocol)]; 6. 7. // 拿到vc后,设置它的属性,因为在BViewControllerProtocol里有定义对应的属性,所以不会有warning 8. vc.backgroundColor = [UIColor redColor]; 9. vc.currentIndex = 1000; 10. [self.navigationController pushViewController:vc animated:YES];
可以看到这里没有引用BViewController。
再来看看这个BViewControllerProtocol是如何注册到injector中的,这里涉及到了Module,对Protocol的注册都是在Module中完成的。Module只要继承JSObjectionModule这个Class即可。
Ios代码
1. @interface BViewControllerModule : JSObjectionModule 2. 3. @end 4. 5. @implementation BViewControllerModule 6. 7. - (void)configure { 8. [self bindClass:[BViewController class] toProtocol:@protocol(BViewControllerProtocol)]; 9. } 10. 11. @end
复制代码
绑定操作是在configure方法里进行的,这个方法在被添加到injector里时会被自动触发。
Ios代码
1. // 获取默认的injector 2. JSObjectionInjector *injector = [JSObjection defaultInjector]; 3. 4. // 如果默认的injector不存在,就新建一个 5. injector = injector ? : [JSObjection createInjector]; 6. 7. // 往这个injector里注册Module 8. injector = [injector withModule:[[BViewControllerModule alloc] init]]; 9. 10. // 设置该injector为默认的injector 11. [JSObjection setDefaultInjector:injector];
复制代码
上面这段代码可以直接放到 + (void)load里执行,这样就可以避免在AppDelegate里import各种Module了。
因为我们无法直接获得对应的Class,所以必须要在协议里定义好对外暴露的方法和属性,然后该Class也要实现该协议。
Ios代码
1. - (void)viewDidLoad { 2. [super viewDidLoad]; 3. 4. UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem]; 5. button.frame = CGRectMake(100, 100, 100, 30); 6. [button setTitle:@"PUSH" forState:UIControlStateNormal]; 7. [button addTarget:self action:@selector(buttonTapped) forControlEvents:UIControlEventTouchUpInside]; 8. [self.view addSubview:button]; 9. } 10. 11. - (void)buttonTapped { 12. ViewControllerB *vc = [[ViewControllerB alloc] init]; 13. [self.navigationController pushViewController:vc animated:YES]; 14. }
通过objection实现依赖注入后,就能更好地实现SRP(Single Responsibility Principle),代码更简洁。