一、前言
随着业务不停地迭代,优酷 APP 用于分发视频资源的 UI 控件越写越多,也越来越复杂,并且同时相似相近的代码也非常多。仔细研究之后,发现是很多耦合导致的问题:
1)布局代码耦合数据模型,相似布局组件各自一套布局代码;
2)数据模型、UIView 继承关系太长,改动时牵一发而动全身,为保险计不得不自立门户;
3)依赖引入,一个组件在另一 bundle 下使用时将引入连串依赖。
有鉴于此,我们需要寻找一种能够进一步降低通用能力接入门槛,提升单个组件的开发效率;进一步降低组件与页面的耦合,建立各类组件的在不同页面的通用投放能力的架构。
二、插件化页面架构的探索
我们先来看一份 ViewController 代码节选,ViewController 内实现 3 个 feature 分别是 A,B,C,并且这些稍微复杂的 feature 无法一次性单步完成(具体一点的话,可以联想成这是一些用户交互的 feature、网络请求等),在某一时机触发,接着在某回调完成余下操作,最终构成了一个完整的 feature。
@implementation ViewController
- (void)viewDidLoad {
[featureA step1];
[featureB step1];
[featureC step1];
}
- (void)callback_xxx {
[featureA step2];
[featureB step2];
}
- (void)callback_yyy {
[featureC step2];
}
@end
这是一种基本的代码组织形式,但是面临着两个痛点:
一是依赖爆炸问题,每接入一个 feature 就无可避免地引入一批依赖,当 feature 数量上去之后,光是 import 语句都好几十行;
二是代码分散问题,同一 feature 相关代码分散在各处 callback,复用到另一 ViewController 或者将其废弃下架都必须要求开发者对该 feature 每一步骤甚至每一行代码都极为熟悉。如何才能解决上述痛点是我们在做架构蓝图时的一个突破口。这时,试图把围绕 ViewContorller 的代码组织形式转变成围绕 feature 代码组织形式,那么就可得到下面 3 段代码节选:
@implementation FeatureA
- (void)recvViewDidLoad {
[self step1];
}
- (void)recvCallback_xxx {
[self step2];
}
@end
@implementation FeatureB
- (void)recvViewDidLoad {
[self step1];
}
- (void)recvCallback_xxx {
[self step2];
}
@end
@implementation FeatureC
- (void)recvViewDidLoad {
[self step1];
}
- (void)recvCallback_yyy {
[self step2];
}
@end
不难发现,代码经过重新组织之后分散的问题已经迎刃而解。依赖爆炸的问题在单个 feature 上来看,多个依赖已收敛到 feature 内部,接入 feature 的时候依赖已从 N 个降至 1 个,只要使用得当的方式,也可把最后一个依赖也一并消除。
此时需要发挥一下我们的想象力,把每个 feature 想象成是一个电器,它们都配有统一规格的插头。
ViewController 好比一个插线板,电器无论插在哪个板上也是可以工作的。推而广之,不仅 ViewController 是一块插线板,任意一个类也看看作为一块插线板,它们的功能