本节书摘来自华章计算机《Swift iOS应用开发实战》一书中的第3章,第3.1节,作者:刘铭 著, 更多章节内容可以访问云栖社区“华章计算机”公众号查看。
3.1MVC设计模式简介
要想成为一名优秀的iOS程序开发人员,至少要具备一种面向对象程序设计语言的开发经验,比如Java、C++或C#,并且C语言也是我们必须熟练掌握的,在此基础上学习Swift语言就会游刃有余。除此以外,我们还要对设计模式有一定的了解。在iOS中最重要的,也是使用最多的一种设计模式就是:模型-视图-控制器(Model-View-Controller,MVC)设计模式。
在开发面向对象应用程序之前(甚至是面向对象程序开发成为主流后的一段时间),程序员们在编写程序代码时还总是愿意将用户界面代码、应用程序逻辑和数据处理代码混合在一起。其好处就是可以快速完成一个初期的项目,但是后期维护和升级时会让整个项目变得举步维艰。
例如,假如你的项目团队为Windows平台开发了一个文本编辑器,但是当需要将它移植到Mac OS平台的时候,我们不得不将基于Windows用户界面的代码从数据和逻辑代码中剥离,然后替换成相应的Mac OS用户界面代码,难度可想而知。但是再将其移植到Web平台呢?还需要经历这一痛苦的过程。要想完成这个“壮举”,往往需要付出非常大的努力,并且讨论到最后可能最省事的方法会变成独立为每个平台重写所有的代码。
MVC设计模式的目的就是将应用程序的用户界面代码、程序逻辑和数据处理代码彼此分离。在这里,Model封装了应用程序的数据,View负责呈现和管理用户界面,Controller则提供应用程序的基本逻辑,并作为Model和View的“中间人”。当用户与View进行交互时,Controller可以命令Model修改数据。当Model数据发生改变时,Controller也要通知View进行界面更新。这样做的意义在于Model与View之间不会发生任何联系,Model只是负责存储和处理数据,当控制器调用数据时,Model只要完成自己的任务就好。
在Swift语言中,我们所创建的任何类都属于下面这三个类型中的一个:视图对象(View)、数据模型对象(Model)或控制器对象(Controller)。通常一个应用程序不仅限于只有一个Model、View和Controller。在Swift中我们通常把控制器叫作视图控制器(View Controller)。
视图控制器与数据模型对象之间的交互主要通过数据模型对象中的公共方法和属性,这与在面向对象(OOP)环境中对象之间的调用相同。
视图控制器与视图之间的交互会稍微复杂一点,需要借助Outlet和Action通过目标(Target)-动作(Action)模式实现,在第2章的学习中我们已经接触到了。
简而言之,MVC设计模式要求在iOS应用程序开发的过程中,所有的对象必须属于Model、View、Controller三大“阵营”中的一个。
视图对象是负责用户界面的对象,比如在Calculator项目的故事板里面,View Controller场景中的Label和Button都是视图对象。一般来说,视图对象是UIView类或其子类,比如UILabel、UIButton、UISlider等。当然,如果需要,还可以自定义一些视图类对象,如BackgroundView、ShoppingView等,相信看到这些类的名字,你就能够猜出它们是负责呈现信息的视图对象。
数据模型对象是负责处理数据的,它与视图对象之间不发生任何关系。在本章的实践中,Model对象只是操作数(firstOperand和secondOperand)、操作符(operatorFlag)等变量。
作为数据模型对象,它们通常是一些标准的集合对象(如NSArray、NSDictionary和NSSet类)和一些标准数据对象(如NSString、NSDate、NSNumber类)。对于大型或复杂的数据模型,往往需要创建自定义类并伴随着数据库的支持,例如Shopping、Employee等。
如果把应用程序看作一座工厂,那么视图对象和数据模型对象就好比是工厂中不同分工的工人,他们都只负责做好自己应该做的事情。例如,Label对象只负责在用户界面中的指定位置显示一个指定尺寸的文本信息,至于文本的内容它并不关心,到时候“会有人告诉他”。而String对象只负责存储一个字符串,至于这个字符串用在哪里他并不关心,到时候“会有人来指定”。
控制器对象用来管理应用程序,它负责视图对象和数据模型对象之间的联系与同步,控制着在程序中传递的各种信息流。比如Calculator项目,当用户点击屏幕上的数字按钮以后,数字按钮(View)会把该事件报告给视图控制器(项目中的ViewController类),视图控制器再调用数据模型对象中的方法或属性。当控制器从数据模型对象处获取结果后,再将信息传递给视图对象(ViewController场景中的Label对象),最终通过Label对象更新屏幕上的显示信息。到此为止,MVC之间的简单工作流程就结束了,如图3-1所示。
从图3-1中可以看出,控制器对象就像中介一样,左右各联系着视图和数据模型。在日常生活中,我们都比较讨厌中介(尤其是那些频繁给你打电话的房屋中介),那么为什么就不能让视图和数据模型直接联系,非要有个中介呢?道理很简单,面向对象编程的优势之一就是类的可复用。如果去掉控制器这个中介,用户在屏幕上的交互操作都直接由视图对象去操作数据模型对象,那么,视图中势必包含了对数据模型的引用语句(#import)。但是,当我们在其他地方复用该视图类时,又可能会包含进其他的数据模型类。也就是说,复用越多,包含不必要的数据模型类的可能性越大。再有,随着程序代码不断升级和完善,在修改数据模型类(比如添加、删除模型类中的属性和方法)以后,就有可能会对视图调用数据模型产生非常严重的不良影响,为应用程序的运行添加很多不稳定因素。
在使用项目模板创建iOS应用程序时,Xcode已经自动创建了一个控制器类(View-Controller.swift文件中定义的类)。需要清楚的是,大部分的应用程序都会含有多个控制器,因为Calculator项目非常简单,所以只含有一个控制器—ViewController。
项目中ViewController的任务就是当用户点击计算器界面中按钮时,触发目标类中的相关方法,从而得到相应的计算结果。