基于 MVC 的项目重构

前言

最近公司的项目要更新所有界面的 UI 风格,趁此机会正好把项目重构一遍,本文主要记录重构时的一些选择和解决的问题。

背景

首先说说背景,也就是为什么要重构,因为重构是需要成本的,一不小心修改错了,就会让原来完好的产品出各种问题,所以先总结下为什么,主要是以下四个问题:

  • 1.没有统一的代码风格
    这个就比较痛苦了,因为历史的原因,或者说这个项目初期就没有代码规范,在接手项目前,代码风格就非常随意、天马行空。在此期间我重构了一部分,但仍有一大半不规范的代码。
  • 2.臃肿的 ViewController
    这个好理解,一个类几千行代码,应该的不应该的都挤在一起。
  • 3.难以理解的逻辑代码
    业务逻辑代码混乱不堪,看的头疼,还不敢乱删。
  • 4.混乱的类调用
    没有明确一个类该做什么,全都堆在一起。

追求代码质量是一个优秀程序员对自己的要求,所以趁这个机会,决定把整个项目重构一遍。

架构的选择

当前在 iOS 开发上有很多热门的架构模式,如 MVC、MVVM、MVCS、VIPER 等等。对此我的观点是:架构的选择要结合具体的情况,比如业务的复杂度、开发人员的接受程度,以及重构的时间周期等等,选择一个对的架构比选择一个复杂的架构更为重要。
在老项目里选择的是 MVC,对于项目中的绝大部分场景来说,MVC 没有成为制约业务发展的瓶颈,同时考虑到新项目的学习成本以及重构时间周期的问题,所以在新项目上还是选择了 MVC。

统一的代码风格

无规矩不成方圆,在老项目里由于流程的缺失和开发人员的更迭,一直没有一个统一的编码规范。而在多人开发时,保持统一的编码规范是很有必要的,这样易于保持代码一致性和 Code Review。所以确定了架构之后,接着就是确定编码规范。
下面是编码规范里一些需要注意的点:

命名

使用可读的驼峰命名法去给类、方法、变量命名。
常量应该使用驼峰式命名规则,所有的单词首字母大写和加上与类名有关的前缀。

代码分块

在函数分组和protocol/delegate实现中使用#pragma mark -来分类方法,要遵循以下一般结构:

#pragma mark - Lifecycle

- (instancetype)init {}
- (void)dealloc {}
- (void)viewDidLoad {}
- (void)viewWillAppear:(BOOL)animated {}
- (void)didReceiveMemoryWarning {}

#pragma mark - Custom Accessors

- (void)setCustomProperty:(id)value {}
- (id)customProperty {}

#pragma mark - IBActions

- (IBAction)submitData:(id)sender {}

#pragma mark - Public

- (void)publicMethod {}

#pragma mark - Private

- (void)privateMethod {}

#pragma mark - Protocol conformance

留空白

  • 建议使用tabs 而不是使用空格,tabs缩进使用4个空格,确保在Xcode偏好设置来设置。
  • 文件结束时留一行空白
  • 用足够的空行把代码分割为合理的逻辑块,而不是非常紧凑
  • 不要在一行代码结尾处留空格
  • 更不要在空行(\n)中使用缩进(\t)

代码块缩进

(if/else/switch/while etc.)或者method function 的大括号留在当前行,并前保留一个空格 ,能省略的不要添加

if user.isHappy {
  // Do something
} else {
  // Do something else
}

不推荐

if (user.isHappy )          多余空格
{                  换行位置不对
  // Do something
}
else {
  // Do something else
}

项目层级划分

物理层级

上文确认了项目架构是以 MVC 为主,所以这里推荐使用 MVC + 按业务划分项目层级的方式。具体的如下图:


整个项目主要分为4个文件夹,分别是:

  • AppDelegate: 程序入口。
  • Classes: 存放主要代码文件。
  • Resources: 存放资源文件,如图片、音频、视频、 HTML 文件等。
  • Supporting Files: 存放配置文件,如 Info.plist、main.m、pch 文件等。

而其中 Classes 目录下,又细分如下图:


  • General:存放一些通用的类,如基类、Category、Model 等。
  • Sections:按业务模块细分 MVC。
  • Helpers:存放各种工具类。
  • Venders:存放需要手动引入的第三方库。
  • Macro:存放全局头文件,各种宏定义,常量等。

代码逻辑层级

从代码逻辑上,大致分为 5 层

  • 网络层:负责和服务器通讯获取数据。
  • 数据层:存储用户的数据,包括内存cache。
  • 业务层:包含各种业务逻辑。
  • UI数据层:负责提供UI层所需要的数据,UI只和这层打交道。
  • UI层:包括ViewController和View,处理用户的输入。

分层使项目更清晰,开发时做到各个层独立,高内聚、低耦合。

布局规范

AutoLayout

老项目是纯 Frame 布局,代码里有一大堆 Magic Number,一大堆屏幕尺寸判断,而这些造成了项目里有过多的布局代码,对于刚上手项目的人来说也不太容易看懂。所以这次重构整体采用 AutoLayout 布局,摒弃老项目里的纯 Frame 布局方式,精简代码。

Xib/Storyboard

手写 UI 和 Xib/Storyboard 谁优谁劣这里不做讨论,但有一点我认为 Xib/Storyboard 是比手写 UI 要快的。而这次重构工作量大工期紧,所以摒弃老项目里的手写 UI ,改用 Xib/Storyboard 。

精简 ViewController

这一部分我认为是本次重构的重头戏,也是本次重构的主要目的所在,精简 ViewController 里的代码,解决老项目中 Massive ViewController 的问题。
主要工作分为以下几步:

  • 1.保留最重要的任务,拆分其它不重要的任务。
  • 2.拆分后的模块要尽可能提高可复用性,尽量做到DRY
  • 3.提高拆分模块后的抽象度

胖 Model 的问题

上文精简 ViewController 把代码都加入到 Model 里去,所以会造成胖 Model 的问题。对此我的理解是,除了优化外,代码的总量是确定的,一方的精简必然会造成一方的增加,而 Model 在这里是用来帮 ViewController 处理业务逻辑的,也就是说胖 Model 包含了部分弱业务逻辑。胖 Model 要达到的目的是,Controller从胖 Model 这里拿到数据之后,不用额外做操作或者只要做非常少的操作,就能够将数据直接应用在 View 上,从这点上看胖 Model 也是可以接受的。

单元测试

老项目是不写测试代码的,也没有写单元测试的条件。想想一个类堆砌几千行代码,想写也不好下手,但既然重构了,单元测试也得补上。
单元测试对于目前来说,就是为了方便测试一些功能是否正常运行,还有调试接口是否能正常使用。
有时候你可能是为了测试某一个网络接口,然后每次都重新启动并且经过很多操作之后才测试到了那个网络接口,如果使用了单元测试,就可以直接测试那个方法,相对方便很多。
比如由于修改较多,想测试一下分享功能是否正常,这时候就有用了。或者直接看一些接口返回的数据也会非常直观,不用启动整个工程。

TODO

在这也列举两个接下来可以做的事,先添加到 ToDoList 里。

  • URLRoute

  • 模块化

最后

其实总结起来就三点,尽量保证:

  • Controller 里的代码尽可能的少
  • Model 的功能尽可能的完整
  • View 尽可能独立

就能构建一个容易维护,便于协同的项目。

本文只是在项目重构中总结的一些比较重要的点,并不意味着都是最优解。而我在项目的架构和代码的组织上经验尚浅,若本文有什么错误或是有更好的方法请直接指出,欢迎交流讨论。

Reference

iOS应用架构谈 view层的组织和调用方案

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

推荐阅读更多精彩内容