iOS 依赖注入

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 源码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • 什么是依赖注入呢? 依赖注入(DI)是一种非常流行的设计模式在许多的语言之中,比如说Java和C#,但是它似乎并没...
    木易林1阅读 1,411评论 0 0
  • dependency injection 关于IOS依赖注入那些事 本文介绍的是另一个屎上最牛叉的ios开发新框架...
    十三亿少女梦丶阅读 9,717评论 1 44
  • 要使用工具, 首先还是先来了解一下为什么要使用它? 而这里有一篇很好的文章说明为什么要进行依赖注入, 以及一些相关...
    貘鸣阅读 6,151评论 3 12
  • 源码 依赖注入(Dependency Injection)这个词,源于java,但在Cocoa框架中也是十分常见的...
    秋刀生鱼片阅读 2,656评论 4 12
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,598评论 18 139