iOS实践:一次失败的重构记实
Feb 20, 2016 · 1 minute read · Comments开发iOS
iOS实践:一次失败的重构记实
摘要:
- 2月15日,原代码分析,研究重构点
- 2月16日,尝试进行自适应和图片加载重构
- 2月17日,重构进行中
- 2月18日,UI改造。
- 2月19日,继续改造(图片逻辑处理方案以及一些感想)
- 2月20日,图片加载模块重构
2月15日,原代码分析,研究重构点
严重问题:
多类型列表的数据源控制,不仅使用了多个数组,还使用了一个包含所有数组的大字典来做统一管理。导致以下问题
- 阅读分析困难。命名的随意性更加加重了对代码的分析阅读,很难找到 相互关联的 UI与 数据源。可以理解为,视图和控制器虽分离了,但关联他们的接口混乱,导致无从查起。
- 数据管理困难。数据源相关的类皆为类属性(成员变量),大量方法函数对6个数据源列表进行操作,严重增加调试和查找困难。
- 数据类型多和混乱。通过大字典管理各个列表,然后就完全没涉及各个类型的数据的关系,然后共享出了问题。更直接的是,没有继承,整个界面就是一堆数据往里面塞得感觉。
有意思的是,我本以为UI部分也会这样。但事实上,比我想象的要好非常多。有类型区分,有管理。UI的主要问题是没用自动布局,及其严重的增加了成本。尤其是对于动态的内容高度等计算。
- 控件的布局困难。没有控件间的约束,导致控件之间的布局特别容易自己玩自己的去了。
- 列表单元高度计算困难。过于动态的内容,导致控件过于复杂的计算。iOS系统中居然没有自适应这种东西,简直让我这个android工程师极度抓狂。
- 单元模型设计的混乱。这个倒是可以理解,由于业务叠加的方向更改的任意性,产品变动的不可预测。直接导致单元模型的混乱,但也太混乱了。
解决思路参考:
- 数据方面。
- - 数据源管理使用单一列表,不适用多列表加字典的方式
- 重新搭建继承关系。通过类型区分各个模型和单元
- 数据的传递和共享可以通过回调处理,不需要硬塞。
- UI方面。
- - UI整体使用自动布局和约束来替代现在的写法。
- 引入自动适应的label方案,减少对于label文字内容的适应问题。
- 引入列表单元自动适应高度解决方案。真心想告别计算单元高度的日子。
分析自适应解决方案
打算引入SDAutoLayout这个三方自适应。初步研究后,发觉能较好的适应tableView的自适应高度和Label的自适应高度。而且这个项目现在有专门人员在github是维护,且项目更新频率很高。
2月16日,尝试进行自适应和图片加载重构
数据方面的重新梳理和关系重构。
原来的数据model类中居然包含单元格高度属性…也是够了
这是一个典型的view和model未分离的情况。
重新命名,并修改内部相关属性。
这个世界上,总有一些傻逼是你躲不过去的。
现在是下午两点,简单说一些上午到现在做的一些东西。
确认使用框架后,先对详情页的主贴和续帖进行重构。接下来进行了几项尝试。
在原CellView上进行修改。相继出现多过崩溃异常。放弃
重新撰写简单的CellView(只包含title 和一个 imageview)。进行调试,文本的自动适应和图片的自动适应相继成功。
重新尝试1,但先进行cellview中,更改为autolayout,再进行自适应。
相继出现各种不出现,同时由于方法相互关联太多,越更改越困难,调试也更加困难。
- 重新尝试2,即从刚才较为简单的模型上添加控件,丰富界面,然后迁移点击事件。
好吧。还是失败了。工作量太大,而且迁移的过程中存在大量的自定义view,导致需要查看大量自定义view,显得不是很现实。再想别的办法。
2月17日,重构进行中
帖子详情页里,主贴,细小的功能非常之多。比我想象中的要困难很多。文本,文字很多需要转富文本,而且同时,还支持各种各样的点击。
主要的图片问题,终于不得不面对了。帖子详情页的图片加载异常混乱,每次加载完一个图片,就会调用界面重绘,极大的加重了UI负担和界面不可控。也是引起帖子详情页加载会崩溃的原因。
所以,暂时决定暂停UI,先行处理图片加载问题。
比我想象的还要困难。略有小感:
重构中困难的是取舍,原先代码是很糟(不糟谁重构啊),但却不能一下子全部抛弃,如果全部推翻重写的成本过于昂贵。
所以,预想中逐步重构,一点一点推翻。
但问题又来了,原先耦合性高且冗杂,加上复杂的业务逻辑,导致改一点就会引起新的问题,又要填补新的解决方案,反而被重构拖累,逐步更改的成本就会越来越高。
这是一个度的问题,但问题是,谁TM告诉我,这个度在哪里!!!
打算使用较为优秀的SDWebImage框架。Demo测试通过,开始集成到项目中。
集成过程异常艰辛。
- 通过pod包管理集成,出现异常。有重复类版本相撞无法通过编译。
- 艰难查实的确存在,但置于一个叫MW…的文件中的lib文件夹下。
- 简单查阅叫MW…这个鬼的用处,根本找不到用在哪,心好累,又不能直接暴力删除。也是够了!
- 直接替换源码试试。 试验失败了,直接替换掉源码,导致另一些类报错,数目很多。估计是对源码进行了加工修改。只能放弃。
妥协方案,用较旧的三方暂时实现图片加载。
失败:
重新回到自适应上,虽然有了一套对于自适应使用的流程代码,但对于复杂又多类型又过于动态的列表仍旧不能适应。需要补充的判断实在是过于复杂,所以,这期自适应,预计将搁浅。
先在较为简单的列表里试验,然后再着手处理复杂还多图的列表吧。
2月18日,UI改造。
改了快一天UI,感悟重申:想要一层层重构,最大的难点其实是对原代码的解读上。最大的成本也是。因为糟糕的代码配合特别动态的逻辑,衍生出来的复杂程度可见一斑。
今天遇到的巨大的问题,更改一处UI,会衍生新的UI问题,更改新的UI问题,又可能会衍生出新的问题。遇到了重构需要换多少血才行,当然是全换最好,可时间和人力成本却不可负担。如果只是更换部分,就会引发多米骨牌,连续的问题会让重构变成焦油坑。
iOS的视图,容器对控件或者父类视图对子类控制能力太弱。列表视图居然需要手动计算每个具体单元高度,这让我非常难以接受,如果计算出错,可能会导致上一个单元的内容遮盖下面单元格的内容,这点让我更加难以接受。同时也让我在进行抽离时,如履薄冰,不知道动哪里会影响到相关方法。
…
…
大概遇到两个不得不思考解决的适应问题。
- 图片载入对UI刷新的影响。
- 文字控件对文字是否能自适应高度。(文本控件居然没有一个属性,自适应高度。这不是刚需吗?程序设计应该要有的啊?想不明白!!!)
UILabel的适应解决方案:
//初始化添加属性
label.numberOfLines = 0;
label.lineBreakMode = NSLineBreakByWordWrapping;
// 填入文字后添加
label.text = data.content
[label sizeToFit]
//注意:只有有宽度的label才能自动分行适应。
2月19日,继续改造(图片逻辑处理方案以及一些感想)
对图片进行改造。
粗定一个图片改造计划。
图片服务交给第三方,真的要交给第三方!现在三方对于图片的服务和管理真的已经很优秀了。我们是基于又拍云的服务提供。
——血泪教训
- 不改动原先代码核心逻辑。一是平稳,而是原先代码的编写过于复杂,阅读成本就非常高,更改成本就更不可控了。
原先的工具类,图片加载的方法需要传递八个参数。八个参数!八个参数!八个参数!真是疯了。这里犯了一个较为明显的错误,参数太多!
方法参数是趋向于越少越好。参数目的是为了控制方法的实现能力,参数越多,实现的能力越强,但实现的情况和结果也越多,而且,每增加一个参数,对于实现出来的结果可能性都是成倍的增加,对修改成本也是很大,无从下手,也无法修改,巨大的不确定性,让这个方法理解和使用都有巨大的成本,对于随后维护的工程师更是大大大大的成本。
所以,方法的参数,没有自然是最优。一个参数,很漂亮,也很自然,基本也不用具体看方法实现。两个参数,已经开始多重方向的控制了,有时需要进入方法具体实现查看。三个参数,必须进入方法具体实现来查看,而且,找三个参数扔进这个方法已经是高额成本了。四个参数,对不起,你写的这个方法有问题。
当然,参数越积越多总是有原因的。一个很大的原因是,参数间本身是有关系,或者是协同一起去控制方法走向的,这时候,应该将几个协同合作的参数封装成一个类,以类的形式对外暴露。这是最直接也是最有效的重构。
- 只更改从又拍云上获得图片逻辑,并与原代码逻辑脱离,单独新建方法,并封装新类进行新的图片逻辑撰写。当然,新方法不支持从老式图片服务器拉图片。
对旧系统的维护和支持要逐步进行替换和更迭。这两年花费了大量时间用户进行旧版本的适配支持,但淘汰率在时间上却很快。成本沉没率很高。现在想想也对,需要对旧版本进行适配和技术提供,就需要更多的时间去设计和修改问题,但这段时间里,新旧迭代的时间却也很快。大概最明显的例子,就是对于iOS 6789的支持了。
- 随后的新功能,新代码将缓存和加载这件事情上交给三方。
随后会再写一篇文字,关于我们到底用不用开源,用多少好。都说适度的使用较好,尼玛!到底什么叫适度,我会写一篇自己的理解出来。
原本的代码是通过类扩展的方式实现的,所以直接在类扩展里加入了新的类扩展方式去实现。然后调用代码也简化了。基本功能已经跑通。
2月20日,图片加载模块重构
昨天已经跑通的基本功能,今天主要是对于图片加载重构的设计和规范。
用了比较直接暴力的方法,直接引用使用,所有的缓存复用等交给依赖包。
具体参考图片加载设计和规范
然后,便进入了重构过程中最最无聊的体力活阶段了。