ios常用功能

iOS 依赖注入

2017-02-22  本文已影响250人  杨柳小易

iOS 依赖注入

最近读项目代码的总结!

什么是依赖呢,会有什么问题呢?平时写代码,这种依赖的方式太常见了。

看代码的时候发现,现实的代码还有这种a包含b,b包含a.然后a b中各自公开很多函数,然后相互各种调用,这是目前我们工程中存在的。

举个栗子 这是一个视频直播的界面Demo,这是一个真实存在的例子栗子


NS_ASSUME_NONNULL_BEGIN

@interface XXXLiveHouseController : UIViewController
/// 播放器界面
@property(strong, nonatomic) XXXLiveVideoController *playerController;
/// 弹幕界面
@property(strong, nonatomic) XXXRiverRunCommentController *riverRunCommentController;
///全屏的控制界面
@property(strong, nonatomic) XXXFullscreenVideoControl *fullscreenVideoControl;
/// 礼物展示界面
@property(strong, nonatomic) XXXGiftView *normalGiftView;
///
···这中间还有很多歌类似的属性
///
@end

NS_ASSUME_NONNULL_END
#endif /* LiveHouseController_h */

在<code>viewDidLoad</code>中,会对以上N个<code>property</code>进行 new,对property自己的属性也会进行相关设置,<code>viewDidload</code>大概有200 多行是创建property的、闭上眼睛感受一下。。

别笑,这个是很是存在的代码。

如果你想对某个property 进行单元测试, 对不起,臣妾做不到。 比如,<code>XXXFullscreenVideoControl</code>,全屏播放的界面上的挂件是很多的,比如,暂停,刷新,表情,输入框,等等等,已经大部分以来在这个界面了。

如果下一个版本的需求过来,想修改全屏播放的界面,基本也是在两个界面的代码之间进行搜索查找。然后看看哪里有依赖。上线之前完全靠强大的测试排除问题。

经历过好几个版本的迭代,依赖已经完全渗透在各个角落。

测试驱动写代码。写可以删除的代码,从学习依赖注入开始。

依赖,通俗易懂的讲,就是两个不同的对象之间产生了相互依赖。如果依赖过多,后期写测试用例,维护,改版都是很大的成本。

开始嘟嘟嘟概念,来自

概念内容来自git:here

依赖####

Java代码
如果在 Class A 中,有 Class B 的实例,则称 Class A 对 Class B 有一个依赖。例如下面类 Human 中用到一个 Father 对象,我们就说类 Human 对类 Father 有一个依赖。


public class Human {
    ...
    Father father;
    ...
    public Human() {
        father = new Father();
    }
}

仔细看这段代码我们会发现存在一些问题:
(1). 如果现在要改变 father 生成方式,如需要用new Father(String name)初始化 father,需要修改 Human 代码;
(2). 如果想测试不同 Father 对象对 Human 的影响很困难,因为 father 的初始化被写死在了 Human 的构造函数中;
(3). 如果new Father()过程非常缓慢,单测时我们希望用已经初始化好的 father 对象 Mock 掉这个过程也很困难。

依赖注入####

上面将依赖在构造函数中直接初始化是一种 Hard init 方式,弊端在于两个类不够独立,不方便测试。我们还有另外一种 Init 方式,如下:


public class Human {
    ...
    Father father;
    ...
    public Human(Father father) {
        this.father = father;
    }
}

上面代码中,我们将 father 对象作为构造函数的一个参数传入。在调用 Human 的构造方法之前外部就已经初始化好了 Father 对象。像这种非自己主动初始化依赖,而通过外部来传入依赖的方式,我们就称为依赖注入。
现在我们发现上面 1 中存在的两个问题都很好解决了,简单的说依赖注入主要有两个好处:
(1). 解耦,将依赖之间解耦。
(2). 因为已经解耦,所以方便做单元测试,尤其是 Mock 测试。

概念完毕

依赖注入有如下实现方式:

基于接口。实现特定接口以供外部容器注入所依赖类型的对象。

基于 set 方法。实现特定属性的public set方法,来让外部容器调用传入所依赖类型的对象。

基于构造函数。实现特定参数的构造函数,在新建对象时传入所依赖类型的对象。

基于注解。基于Java的注解功能,在私有变量前加“@Autowired”等注解,不需要显式的定义以上三种代码,便可以让外部容器传入对应的对象。该方案相当于定义了public的set方法,但是因为没有真正的set方法,从而不会为了实现依赖注入导致暴露了不该暴露的接口(因为set方法只想让容器访问来注入而并不希望其他依赖此类的对象访问)
在iOS中还可以基于 <code>xib</code>

举例子:
基于构造函数

@interface Car : NSObject
@property(nonatomic, strong) Engine *engine;
@property(nonatomic, strong) Brakes *brakes;
@end

@implementation Car

- (id)initWithEngine:(Engine *)engine brakes:(Brakes *)brakes {
    self = [super init];
    if (self) {
        _engine = engine;
        _brakes = brakes;
    }
    return self;
}
@end

基于<code>property</code>

Car *c = [Car new];
Engine *e = [Engine new];
e.name = @"Y";
c.engine = e;

基于工厂方法

Engine *create () {
    //或许还有其他配置
    return [Engine new];
}

Car *c = [Car new];
//或者其他地方调用工厂方法。总之工厂方法负责产出 Engine
c.engine = create();

基于<code>xib</code>
如图:

1 2 3

此处Car是继承自NSObject的类,所以xib也是解耦的神器。

你看,我们上面那个直播界面,完全可以向上面的依赖注入方式整理的很清楚,我的习惯,如果<code>property</code>的生成方式只是 <code>new</code>的方式,那么就是用<code>xib</code>进行注入,如果<code>property</code>比较少,就通过构造函数进行注入,如果多了,并且不会经常改动,就通过set方法注入,批量用到的就通过 工厂方法生成。

个人喜好,最近喜欢通过类簇来分解几千行代码的类。

当然,这些第三库都有做的很好的。objection 就是其中一个依赖注入很好的库

用法如下:

@class Engine, Brakes;

@interface Car : NSObject

// Will be filled in by objection
@property(nonatomic, strong) Engine *engine;
// Will be filled in by objection
@property(nonatomic, strong) Brakes *brakes;
@property(nonatomic) BOOL awake;
@end


@implementation Car
objection_requires(@"engine", @"brakes")
@synthesize engine, brakes, awake;

- (id)init {
    self = [super init];
    if (self) {
    }
    return self;
}

@end

其他地方就可以这样子调用

JSObjectionInjector *injector = [JSObjection createInjector];
Car *c = [injector getObject:[Car class]];

objection的栗子来自<code>objection</code> git网址,还有很多注入的用法,感兴趣的可以自己查看git.。

至于开篇说的相互包含的问题,我觉得代理就能完美的解决。

说了这么多,就是为了立帖为证,下回督促自己分析 objection 源码

上一篇下一篇

猜你喜欢

热点阅读