iOS组件化开发架构设计思考(初版)

组件化

组件化开发系列:
(一)iOS组件化开发架构设计思考
(二)iOS组件化开发实施一期文档

前言

   我们在谈模块化、组件化,中心路由,响应式编程,三层、四层架构,依赖注入等概念的时候,出发点是什么?应该是项目管理上的稳定性,可维护性,开发效率,需要从业务中提炼出更抽象的结构思想,不要为了架构而架构,流于形式。软件项目开发的整个周期内都应该考虑这些问题,而且是持续性的,关键是我们一定要有设计思想、负责的态度、认真的执行力。具体有必要做哪些,可以根据实际情况,做好预案,逐步实施。

   其实好多事情,我们也可以借势去推动,帮助公司和个人成长。项目的重构,资源的重组,也是资源和权利的再分配。

一. 项目现状

   当前iOS端APP项目大概有35万行代码,早期为了iPad和iPhone双端开发的效率,将所有业务模块的网络请求和数据模型统一抽离到DDEngine工程,自定义了私有开发库DDDevLib工程,和第三方SDKs管理库ThirdSDKs工程,和功能相对独立的DDMIX_UI工程。在项目较小时,这个架构是完全可以满足开发需要的,也是容易推进的。但是开发过程中缺少组件化的考量,随着产品线的扩展和人员流动,对项目不熟悉和开发规范不够严谨,造成业务模块依赖关系非常复杂,主要有三类耦合:
工程耦合:某些模块在运行时需要依赖主工程的代码才能运行或实现完整的功能;
界面耦合:App 执行过程中,硬编码的界面跳转行为;
依赖耦合:两个业务模块之间的代码依赖。

   现在一处改动有可能造成多处受影响,需要测试同事做大量回归测试,存在重复造轮子、项目编译时间长、开发效率降低等问题。目前还只是基于OC编码,Swift编码也是项目演进的一个重要方向,Swift和OC的相互调用还不是很方便,暗坑无数,需要先对项目进行组件化演进,以便项目向Swift和OC混编过渡。

二. 方案调研

参考业内的流行方案

1、基于 URL Router、ModuleMediator

代表:蘑菇街 Limboy

URL组件化架构图

优缺点
   1、URL Router方案,在使用时需要注册模块类,需要维护一个URL路由表,而且路由表一般保存服务端,拓展性和可维护性降低。
   2、非常规对象无法直接参与本地组件间调度,模块之间用URL去完成调度,徒增复杂化。
   3、项目中已有URL路由配置,URL Router 开发起来相对比较容易推进。

2、基于 Target-Action、Runtime、Category

代表:安居客 Casa Taloyum

Target-Action组件化架构图

优缺点
   1、将远程调用和本地调用做了拆分,而且由本地应用调用位远程应用调用提供服务,安全保证,对url中进行native前缀验证。
   2、模块仅通过Action暴露可调用接口,必需去Model设计:只有调用方依赖Mediator,响应方依赖是没有必要的。
   3、模块调用存在一定的硬编码问题,可以通过在模块中维护针对DDMediator的Category,每个category对应一个target,categroy中的方法对应Action场景。
   4、业务发现的模式,存在业务调用的未知性,代码的可读性有所降低。

   主要是基于Mediator模式和Target-Action模式,中间采用了runtime来完成调用。这套组件化方案将远程应用调用和本地应用调用做了拆分,而且是由本地应用调用为远程应用调用提供服务。

3、去model化

   在组件和模块拆分之前,一定要明确一个概念,组件间调用是需要针对参数做去model化的。如果组件间调用不对参数做去model化的设计,就会导致业务形式上被组件化了,实质上依然没有被完全独立。其中的model并不是指架构中的model层,此处model化指的是模块间使用数据对象传值,去model化指的是模块间不使用数据对象传值。

三. 目标设定

组件化开发很重要的目的就是解耦合,提高开发效率。解耦的主要方式:
   1. 把依赖的代码先做成一个Pod库,然后转而依赖Pod库,“依赖下沉”。
   2. 使用category的方式把依赖改成组合的方式。
   3. 使用一个block、delegate(协议)、通知等把这部分职责抛出去。
   4. 直接copy代码。避免业务干扰,也可以直接copy代码来用,谨慎使用。

1、架构设计:层级架构+组件化架构

   层级架构:只能上层对下层依赖,下层对上层不能有依赖,下层中不要包含上层业务逻辑,同层间的功能库或组件之间都应无依赖关系。

组件化层级及拆分

   组件化架构:处于层级架构的最上层,也就是业务层。各业务组件之间通过中心路由转发调起其他组件的服务,并能独立编译、运行。

组件化架构图

   采取这种结构混合的方式进行整体架构,与项目当前的架构比较接近,方便组件化的推进,符合公司业务需求。

2、目标效果

1. 第三方库管理、基础库、功能组件独立

保证所有第三方库管理、基础库、功能组件从主工程抽出,可独立编译,便于各业务模块的调用。提高代码复用率,提高业务开发效率。

2. 业务模块划分与拆解,独立编译

将业务按对应用功能或流程进行划分和拆解,能够单独进行编译、运行。保障各业务线开发相互独立,减少业务耦合,方便 QA 有针对性地测试。

3. CocoaPods 发布私有库,减少编译时间

将稳定模块打包成.a文件,做成私有库,并对每个组件用 Pods 进行管理和后续更新工作。加快编译速度,提高业务开发效率。

4. 通过模块库,提高代码重构或搭建新产品线的效率

通过对模块的组合配置或替换,能快速安全的替换旧的模块库,或快速搭建新的产品线,或将模块库扩展到其他产品线。

四. 制定计划

   首先要明白,不是重写而是重构,是不改变软件功能的前提下,调整软件内部结构,优化项目管理,规范开发流程,提高开发效率,让代码更易读,更好维护。
原则: 稳定、精简、能够重用、持续进行,最大程度的让框架和业务分离,功能和业务分离。

1、在原项目的基础上调整?

1.目前的结构经过改进还能满足开发需要,项目扩展,没必要“推到重来”。
2.项目一直在迭代开发,没有足够的人力和时间,去重写项目,重写项目的周期太长,并不能随时中断。
3.没有单元测试,人员变动较大,熟悉业务的同事不多,重写的风险极大。
4.项目历史悠久,隐含配置较多,对旧系统的兼容配置,另外多是接管的代码,代码也没有注释,甚至注释不匹配的情况。
5.逻辑相互耦合,拔出萝卜带出泥,迁移模块时,关联太多。
6.有可能过度设计,导致重写计划迟迟无法完成,也不能优化工作用到项目中。

总之,在原项目的基础上逐步改进

2、风险评估控制

  1. 开始前预估工期,预估重构风险。
  2. 修改时,拉去新分支,要做到代码隔离。
  3. 并详细做好重构的细节记录,如文件移动,文件改写等重要操作文档化,便于review,问题回归,测试回归。
  4. 每一个拆分出的模块及时添加文档,每一个模块或组件的建立者把模块内容、拆分目的、设计思路等基本信息记录一下,有什么坑或者注意点也可以文档化,是以后的长期项目维护成为可能。

3、组件化应分步推进

组件化是一个项目的整体调整,需要分阶段,分批次的逐步重构。
每个阶段大概的步骤:
1.明确该阶段工作的重点,预估工期,预估重构风险。
2.拉取新分支,隔离重构代码。
3.阶段重构调整之后,需要半数组员做代码review。
4.提交测试回归相关功能。
5.将重构代码整合到最近的发版中。

五. 实施计划

1、一期:梳理项目工程结构,抽离基础层和中间层

1. 项目工程定义

初步调整工程目录结构如下:

基础层和中间层

Pods: 仅Pods管理的第三方库。
DDManualThirdSDKs: Pods不能管理的第三方库,对第三方进行必要的封装,集中做成私有库的形式,再交给Pods管理。

DDDevelopLib: 自行封装的私有库,如系统类分类、数据处理类、通用UI组件、宏定义和常量定义等。做成私有库,交给Pods管理。

业务组件层

DDEngine: 后续会把模型规整到具体模块中,部分文件需要抽离到基础层或中间层,其他暂时保持不变,后续抽离封装网络库。
DDMIX_UI: 暂时保留,属于三期。
ddDemo: 将其中的第三方库、UILib、分类等,下沉到基础层或中间层外,暂时保持不变。

2. 整理三方库统一Pods管理

把项目中散落的第三方库都规整到Pods和DDManualThirdSDKs工程中,优先Pods管理。
散落的第三方库有:iflyMSC、libSunFlower、DDReader、YYText、MiaoZhen、NTalkerUIKitSDK、TalkingData、VoiceConvert、HPGrowingTextView(7年未维护了,局部使用,可以内化为自行代码)等。

3. 梳理DDDevelopLib私有库

自行封装的私有库,作为整个工程的基础库,不与具体的业务耦合,如系统类分类、数据处理类、通用UI组件、宏定义和常量定义等。做成私有库,交给Pods管理。如ddDemo中的VerifyUpdate、UILib、DDAppkit、Common、Categorys、Function、DDMonkey等。

4. Resources文件整理

初步整理Resources资源文件,后期会根据资源情况再统一整理图片资源,注意资源文件的加载路径的变化后,保证资源文件能正常加载。

5. SupportingFiles文件整理

创建真实目录SupportingFiles,存放系统支持文件如下:main.m、Info.plist、Prefix.pch、Debug.entitlements。

2、二期:梳理中心路由,搭建组件开发的模板框架

1.梳理跳转中心
将跳转中心的路由按业务组件进行分组。

2.创建一个组件的模板框架
每个组件,都有一个组件服务管理类,来管理对外能提供的服务列表和使用说明。
组件内的架构设计,使用MVC、MVP还是MVVM,根据各自情况合理选择就好了。

3、三期:将组件化,扩展到整个项目,将稳定组件用pod管理

  1. 将稳定的组件打包成.a文件,进行pod管理。
  2. 将组件化,扩展到整个项目。

六. 常见问题及对应思路

1、组件化工时不好评估,工作进度难以控制

每个阶段开始前都要做好技术调研和探讨,少走些弯路,降低返工率。制定较为详细的计划表,做好工作分配,处理好业务开发与技术改造工作之间的关系,保障组件化能连续的进行。

2、分层结构不明确和横向依赖

业务模块之间也存在一些不合理的横向依赖,没有进行一个合理的业务边界划分。这些原因导致我们在进行拆分工作时经常需要回过头来对已经拆出来的模块和组件重新进行整理和处理,重复劳动量很大。采用先易后难的步骤,尽量下沉逻辑,横向依赖,可以做一个下层抽取,变横向依赖为上下层依赖。

3、拆分粒度大小不好把控

如单品页组件中有单品主页,评论页,写评论等较大的模块,拆分力度不够细,单品页的信息会交织在一起。前期可以将业务关联性强的模块集合在一起,后期在逐个组件做细分拆解。

4、注意对代码调整的控制

拆分过程中,会有重复代码,甚至无用代码,在做调整时要谨慎,尤其是公共类,保障逻辑不变的情况下酌情调整,设置不做修改,并做好记录,方便review,请测试回归。杜绝随意改动,也不回归测试。

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

推荐阅读更多精彩内容

  • 为什么需要组件化随着公司业务的不断发展,项目的功能越来越复杂,各个业务代码耦合也越来越多,代码量也是急剧增加,传统...
    阳明先生_x阅读 6,308评论 2 54
  • 前言 首先我觉得”组件”在这里不太合适,因为按我理解组件是指比较小的功能块,这些组件不需要多少组件间通信,没什么依...
    進无尽阅读 3,515评论 1 14
  • 前言: 本文转自前同事casa的博文,这篇文章是基于runtime实现的iOS组件化方案,其实iOS组件化方案基本...
    monkey01阅读 1,651评论 1 2
  • 最近在学习vue.js的时候发现,vue的组件化的思想对于编写代码是一个非常有用的事情。 首先为什么需要组件化? ...
    拂晓的云阅读 7,155评论 6 23
  • 坐等天亮,这已经不是第一次了。又粗又硬的引流管从腋下穿入肺腔(自创词,不知是不是叫肺腔),身体稍微一动,引流管就会...
    写心之所想阅读 468评论 0 0