一、MVC设计模式
MVC,全称是 Model View Controller,是模型 (model)-视图 (view)-控制器 (controller) 的缩写。它表示的是一种常见的客户端软件开发框架。现在,MVC 已经成为主流的客户端编程框架,在 iOS 开发中,系统为我们实现好了公共的视图类:UIView,和控制器类:UIViewController。大多数时候,我们都需要继承这些类来实现我们的程序逻辑,因此,我们几乎逃避不开 MVC 这种设计模式。但是,我们对于 MVC 这种设计模式真的用得好吗?其实不是的,MVC 这种分层方式虽然清楚,但是如果使用不当,很可能让大量代码都集中在 Controller 之中,让 MVC 模式变成了臃肿的 View Controller 模式。
1.1、Controller的臃肿问题
MVC 这种架构的特点,其实设计模式很多时候是为了 Don't repeat yourself 原则来做的,该原则要求能够复用的代码要尽量复用,来保证重用。在 MVC 这种设计模式中,我们发现 View 和 Model 都是符合这种原则的。
对于 View 来说,你如果抽象得好,那么一个 App 的动画效果可以很方便地移植到别的 App 上,而 Github 上也有很多 UI 控件,这些控件都是在 View 层做了很好的封装设计,使得它能够方便地开源给大家复用。
对于 Model 来说,它其实是用来存储业务的数据的,如果做得好,它也可以方便地复用。
对于controller来说,如果我们能够意识到 Controller 里面的代码不便于复用,我们就能知道什么代码应该写在 Controller 里面了,那就是那些不能复用的代码。在我看来,Controller 里面就只应该存放这些不能复用的代码,这些代码包括:
- 在初始化时,构造相应的 View 和 Model。
- 监听 Model 层的事件,将 Model 层的数据传递到 View 层。
- 监听 View 层的事件,并且将 View 层的事件转发到 Model 层。
如果 Controller 只有以上的这些代码,那么它的逻辑将非常简单,而且也会非常短。但是,我们却很难做到这一点,因为还是有很多逻辑我们不知道写在哪里,于是就都写到了 Controller 中了,那我们接下来就看看其它逻辑应该写在哪里。
1.2 对controller进行减肥
1.将网络请求抽象到单独的类中
新手写代码,直接就在 Controller 里面用 AFNetworking 发一个请求,请求的完数据直接就传递给 View(我承认自己也是一个菜鸟!)。入门一些的伙伴,知道把这些请求代码移到另外一个静态类里面(比如说我们常用的分类:category)。但是我觉得还不够,所以我建议将每一个网络请求直接封装成类(常见的网络请求工具类)。
把每一个网络请求封装成对象其实是使用了设计模式中的 Command 模式,它有以下好处:
-将网络请求与具体的第三方库依赖隔离,方便以后更换底层的网络库(如果你看过杰哥的微博视频,你就很容易想起这是他所说的分层思想:什么工具类,数据类,业务类等等)。
-方便在基类中处理公共逻辑,例如猿题库的数据版本号信息就统一在基类中处理。
-方便在基类中处理缓存逻辑,以及其它一些公共逻辑。
-方便做对象的持久化。
这部分代码从 Controller 中剥离出来后,不但简化了 Controller 中的逻辑(controller减肥有好大的进步啊!啥都不想说,先赞一个!),也达到了网络层的代码复用的效果。
2.将界面的拼装抽象到专门的类中
新手写代码,喜欢在 Controller 中把一个个 UILabel ,UIButton,UITextField 往self.view上用addSubView方法放(告诉你一个小秘密:其实我也这么做!)。但是我建议大家可以用两种办法把这些代码从 Controller 中剥离。
方法一:构造专门的 UIView 的子类,来负责这些控件的拼装。这是最彻底和优雅的方式,不过稍微麻烦一些的是,你需要把这些控件的事件回调先接管,再都一一暴露回 Controller。
方法二:用一个静态的工具类,帮助你做 UIView 的拼装工作。这种方式稍微做得不太彻底,但是比较简单。
对于一些能复用的 UI 控件,我建议用方法一。如果项目工程比较复杂,我也建议用方法一。如果项目太紧,另外相关项目的代码量也不多,可以尝试方法二。
构造viewModel
谁说 MVC 就不能用 ViewModel 的?MVVM 的优点我们一样可以借鉴。具体做法就是将 ViewController 给 View 传递数据这个过程,抽象成构造 ViewModel 的过程。
这样抽象之后,View 只接受 ViewModel,而 Controller 只需要传递 ViewModel 这么一行代码。而另外构造 ViewModel 的过程,我们就可以移动到另外的类中了。
在具体实践中,我建议大家专门创建构造 ViewModel 工厂类,参见工厂模式。另外,也可以专门将数据存取都抽将到一个 Service 层,由这层来提供 ViewModel 的获取。
专门构造存储类
刚刚说到 ViewModel 的构造可以抽奖到一个 Service 层。与此相应的,数据的存储也应该由专门的对象来做。在小猿搜题项目中,我们由一个叫 UserAgent 的类,专门来处理本地数据的存取。
数据存取放在专门的类中,就可以针对存取做额外的事情了。比如:
- 对一些热点数据增加缓存
- 处理数据迁移相关的逻辑
如果要做得更细,可以把存储引擎再抽象出一层。这样你就可以方便地切换存储的底层,例如从 sqlite 切换到 key-value 的存储引擎等。
小结:
通过代码的抽取,我们可以将原本的 MVC 设计模式中的 ViewController 进一步拆分,构造出 网络请求层、ViewModel 层、Service 层、Storage 层等其它类,来配合 Controller 工作,从而使 Controller 更加简单(就是我说的减肥大计),我们的 App 更容易维护。
另外,不知道大家注意到没,其实 Controller 层是非常难于测试的,如果我们能够将 Controller 瘦身,就可以更方便地写 Unit Test 来测试各种与界面的无关的逻辑。移动端自动化测试框架都不太成熟,但是将 Controller 的代码抽取出来,是有助于我们做测试工作的。
希望本文能够帮助大家很好的认识MVC的设计理念,写的不好!还请伙伴们批评指正!