Design Pattern 设计模式是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。设计模式的目的是增强代码的可重用性、可靠性,使代码便于他人阅读和理解。
目前在iOS 开发中常用的设计模式是:MVC模式、MVVM模式、单例模式、代理模式、观察者模式、策略模式、工厂模式。
1.MVC
Model View Controller (MVC)是 Xerox PARC 在 20 世纪 80 年代为编程语言 Smalltalk-80 发明的一种软件设计模式,是苹果推荐的一个用来组织代码,构建iOS App的标准模式。在MVC下,所有的对象被归类为Model、View和Controller。Model负责持有数据,View用于显示与用户交互的界面,而Model和View之间的交互经由ViewController进行调解。
(1)Model
Model(模型对象)持有并封装应用数据,定义操控和处理该数据的逻辑和运算。用户在视图层中所进行的创建或修改数据的操作,通过控制器对象传达出去,最终会创建或更新模型对象。模型对象更改时,它通知控制器对象,控制器对象更新相应的视图对象。
(2)View
View(视图对象)是应用程序向用户直接呈现和并向用户作出响应的可视对象。其主要目的就是显示模型对象的数据,使数据可被编辑。在以往的开发中,视图对象通常与模型对象分离。
(3)Controller
Controller(控制器对象)在视图对象和模型对象之间充当媒介,是同步管道程序。视图对象获悉模型对象的更改,反之亦然。控制器对象为应用程序执行设置和协调任务,管理其他对象的生命周期。控制器对象捕获用户在视图对象上的操作,创建或更改数据并传达给模型对象。模型对象更改时,一个控制器对象会将新的模型数据传达给视图对象,以便视图对象及时更新显示的数据。
MVC 在现实应用中的不足:
(1)控制器对象体积庞大
受限于自身运行的平台终端,移动端不能够像 PC 端一样处理大量的复杂业务场景,而传统App 中存在的模型数据较为简单,不会涉及到复杂的逻辑处理。传统的 Model 数据主要来源于网络,在移动端通过网络获取到这些数据后直接显示在界面上。随着移动端平台业务发展,移动端愈发的要自行处理一部分逻辑计算操作,在传统App中都是由控制器进行处理,随着业务场景的扩展,控制器存在的处理代码越来越多,最终导致其变成了难以维护的垃圾箱。
(2)视图对象参与逻辑处理
视图对象通常是 UIKit 控件或者编码定义的 UIKit 控件的集合,在处理实际处理中我们往往会让视图对对象对所要展示的数据对象的数据进行一些逻辑处理,再进行显示。业务逻辑很明显不归入 view,视图本身没有任何业务。View 不应该直接引用 Model,并且仅仅通过 IBAction 事件引用 controller。
(3)Model与Controller的体积反差
早期的 Model 数据对象普遍只定义了一些属性,在Model类的实现文件中基本上看不到复杂的业务处理和对象的构造,在这种叫小体积下的数据对象与厚重的控制器对象形成反差,这一度让人不禁对现有的开发设计构思有所怀疑。
(4)不利于单元测试
控制器对象整合了视图处理逻辑和业务逻辑,在进行单元测试时不得不面对艰难的分离任务,极大的影响整个开发的效率。
2.MVVM
作为构建iOS App的标准模式,我们已在前文中列举出了MVC几个最突出的问题:控制器对象体积庞大、视图对象参与逻辑处理、Model与Controller的体积反差和不利于单元测试。那么,对于一个较传统App存在更复杂的视图交互和逻辑处理的应用开发时,选择代码耦合性较低且便于维护的设计模式便是我在这里介绍MVVM的目的。
MVVM是从MVC引申出来以分离视图对象和数据对象为主要目的架构模式。是由微软带来的WPF与MVP在应用结合的实践中发展而来,用以应对对客户日益复杂的需求变化。
在基本概念上,MVVM与MVC最大的区别是设立ViewModel用于专门存放业务逻辑的代码,进一步加强视图对象和业务逻辑的分离。通过引入视图模型这样的新组建,为控制器对象进行减肥、降低代码的耦合性并为单元测试提供了可能。
ViewModel(视图模型) 容纳了应用程序的显示逻辑,不仅仅包含显示的数据和可见属性,它还实现了数据对象和视图对象交互的方法,协助控制器对象对界面控件的使用。
上图展示了在MVVM设计模式下的逻辑关系,与上文MVC的图片相比较可以帮助我们了解MVVM的本质是什么。MVVM是一个升级版本的MVC。我们将原本MVC中的View和Controlle的部分逻辑转而交由一个叫做ViewModel的对象来负责处理。它听起来很复杂,但它本质上是一个新版本的MVC。
到这里我已经简单介绍了MVVM以及它和MVC不得不说的关系,当然我不是在怂恿开发者放弃MVC转而都采用MVVM,毕竟MVVM只是一种设计模式,是代码设计经验,需要按需判断。但我们在考虑是否采用或者转换成MVVM时需要清楚以下几点:
(1)MVVM可以兼容MVC
(2)MVVM为单元测试提供对象(ViewModel)
(3)MVVM适合配合绑定机制使用
3.单例模式
单例模式大概是设计模式中最简单的一个,它的定义是:保证一个类只有一个实例,并且提供一个全局的访问入口访问这个实例(Ensures aclasshas only one instance, and provide a global point of access to it.)。它作用是可以保证在程序运行过程,一个类只有一个实例,而且该实例易于供外界访问从而方便地控制了实例个数,并节约系统资源。
单例模式在实际的应用场景中提供了一个为人熟知的访问点,为客户类共享资源生成唯一实例,并通过该实例共享资源。或许我们平时使用中习惯使用全局对象或者类方法来提供一个类似的访问点,但是全局对象无法防止类被实例化一次以上,而且类方法也缺少消除耦合的灵活性。举一个例子,我给整个应用程序定义了一个全局的变量,用于我在编写这个项目工程中共享和访问这个资源,然而因为在App应用场景越来越复杂的情况下,我和同事之间负责的功能或模块必然会有交集,有可能他也在某处定义了一个相同的全局变量,若在后期Code Review中再回头来协同处理这些全局变量在开发成本和效率上会带来的不小的影响。而类方法得作用是提供方法用于共享服务,没有创建可以共享的资源的实例。
4.观察者模式
观察者模式亦称为发布-订阅模式,它的基本概念是:定义对象见的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。它就像是订阅杂志一样,读者把名字和邮寄地址提供给出版社进行杂志订阅。出版社保证按照读者的邮寄地址准确无误地将杂志送至读者手中,没有订阅就不会收到该杂志。观察者模式就是如订阅杂志这样,观察者在出版社-通知器中注册自己到特定的通知产生类似订阅杂志的行为,当有通知发出的时候,观察者通过通知器便能获取到该通知。
观察者模式的思路如结构静态图所示,Subject提供注册和取消注册的方法,任何实现Observer协议而且想要处理update消息的对象,都可以注册或取消。当Subject的实力发生变更时,它会向自己发送notify消息,向已注册的观察者广播update消息。
使用观察者模式的优势是,可以用多个Observer来扩展Subject的行为,这些Observer具有处理存储在Subject中的信息的特定实现。它也可以说是消除不同对象之间的耦合的一种设计模式。当我们在遇上以下情形的时候,可以优先考虑使用观察者模式:
(1)因为一个对象的改变需要同时改变其他对象
(2)一个对象需要对其他的对象进行通知
5.代理模式
在平时生活中,提供免费试用品或试用期是相当常见的促销手段,对于一些价格较贵的商品或者在线订阅服务,这样的促销手段尤为有效。那么引申出来,试用品和试用期就可以当作做代理,代理的一个常见用处是作为一个轻量的替身对象,它允许客户端首先访问某些便宜的信息或功能,直到真真需要使用更有价值更高级的功能时,代理就会为客户端准备真正的“商品”。代理在通常来讲是一种替代或者占位,它控制对另一些对象的访问,而这些对象可能是远程对象,开销较大的对象或者是对安全性有要求的对象。而这一思想细化而来的一种模式即为代理模式。
代理模式是为其他对象提供一种代理以控制对这个对象的访问。它的思想是使用一个基本上跟实体对象行为相同的代理,客户端无需知晓面对的代理是否是一个实体对象,当客户端请求某些创建的开销较大的功能是,代理将把请求转发给实体对象,准备好请求的功能并返回给客户端。
如类图所示,当客户端Client向Proxy对象发送request消息时,Proxy对象会把这个消息转发给Proxy对象之中的RealSubject对象。而RealSubject才是实际实施操作间接满足客户端请求的对象。
iOS对于代理模式的支持很好,因为Objective-C是不支持多继承的,很多时候我们都使用Protocol协议。Protocol协议定义了一套公用的接口,但不能提供具体的实现方法,因为具体的实现方法需要由协议的代理对象去完成。协议可以继承其他协议,并且可以继承多个协议。
首先我们需要了解到协议存在两个修饰符optional和required,在没有特别声明的情况下,协议默认为required,协议的代理对象是delegate。下面我们将用一个简单的Demo来描述Protocol协议:
1.创建协议类 ProtocolDelegate.h
2.创建协议代理对象ProtocolDelegateViewController
3.创建协议委托对象ProtocolViewController
6.工厂模式
工厂方法是我们在应用程序开发中最常见的模式,我们通过具体工厂重载其抽象工厂父类定义的工厂方法来创建自己的对象。工厂方法也成为虚拟构造器。它适用于一个类无法与其生成拿个类对象,想让其子类来制定所生成的对象。因此,工厂模式可以定义为:定义创建对象的接口,让子类决定实例化哪一个类。工厂方法使得一个类的实例化延迟到其子类。
如类图所示,抽象的产品(Product)定义了工厂方法创建的对象借口。具体对象(ConcreteProduct)实现了产品接口。Creator定义了返回产品对象的工厂方法。它也可以为工厂方法定义一个实现,返回默认具体对象。Creator的其他操作也可以调用此工厂方法创建产品对象。ConcreteCreator是Creator的子类,它通过重载工厂方法以返回具体产品的实例。
一般来说,当我们在编译时无法准确预期要的创建的对象的类,类想让其子类决定在运行时创建的产品,或者当类存在若干个辅助子类而我们想将返回的具体子类信息局部化时将会使用到工厂模式。工厂模式能给予类在变更返回哪一种对象这一点上更多的灵活性。
工厂模式让应用程序可以要求由工厂方法创建的对象拥有一组共同的行为。当我们在类层中加入新的具体产品时,并不用修改原代码,因为我们定义创建对象的接口一直不变。
下面我将从一个成衣工厂的Demo来代码实现工厂模式:
1.首先我们定义成衣产品的基类Clothe
2.然后派生出两个子类,衬衣类和T恤类
3.定义工厂基类ClotheFactory
4.派生出两个工厂子类ShirtFactory和TshirtFactory
这两个工厂子类的中,我重载了工厂方法,返回子工厂生产的具体产品对象。
5.子类工厂调用
最终我们在演示用的视图控制器中设定两个按钮,通过分别触发两个按钮来调用子类工厂返回产品对象的方法,并通过界面输出产品名称来查看具体的实际产品。
以上就是这次对iOS设计模式的总结,第一次写文中还有很多不足的地方,后面会加强文章的撰写能力。
这个是本文章DEMO的地址:Github
参考资料
《Objective-C编程之道》
浅谈iOS中MVVM的架构设计与团队协作
我对iOS开发中使用MVVM的理解和使用
iOS 中的 21 种设计模式