iOS中那些精妙的设计模式
2017-03-23 本文已影响1425人
郑明明
iOS开发中,cocoa touch框架是一个非常稳定和成熟的框架,这样的一个优秀框架,必然少不了设计模式的存在。本文将设计模式的讲解过渡到iOS设计中,体会历史沉淀下来的设计模式的精妙
1、工厂模式
- 结构图:
-
是什么:
- 提供一个类似工厂加工的方法,创建具体对象,返回抽象类型
- 具体生成什么类型在工厂内部进行判断
- 最后抽象类型(父类)指向具体对象,作为产品
-
哪里好:
- 工厂方法具有面向对象的优点,古代没有活字印刷的时候,直接进行雕刻,是面向过程的,复用和维护都非常困难,活字印刷是面向对象的,类似工厂方法,维护和复用都很方便
- 将具体产生什么类型隐藏起来,将产生什么类型交给子类决定
-
哪里有:
- NSNumber的API中,有类初始化方法,将会产生两种类型,NSCFBoolean和NSCFNumber,这两个类仍然是工厂,而不属于具体对象,继续往下,NSCFBoolean和NSCFNumber的intValue和boolValue方法将会生成具体的对象,NSCFBoolean和NSCFNumber就是所谓的工厂模式的体现
2、抽象工厂
-
结构图:
抽象工厂
-
是什么:
- 可以是一个接口或者一个类,有很多工厂会实现这个接口或者继承这个类
- 抽象工厂模式相当于多个工厂实现了统一接口或者继承了同一个类
-
哪里好:
- 如果只有一种类型的工厂,那么用工厂模式即可,但是有些情况下会存在同类的多个工厂,这时候就扩展为抽象工厂
-
哪里有:NSNumber调用类初始化方法后,返回的对象是NSCFBoolean和NSBoolean工厂,这两个工厂继承于NSNumber,拥有同样的一些方法,例如intValue和boolValue,在NSNumber的角度看,这就是抽象工厂,同时iOS中把抽象工厂别名为“类簇”
3、装饰者
-
结构图:
装饰者 -
是什么:
- 原始的类和装饰者接口共同实现一个接口(如果只有一个装饰者类的话,那么就可以不用接口)
- 装饰类中有一个接口类型的对象
- 装饰类中有新的方法,内嵌在接口的方法中
- 装饰类对象和原始类对象都可以作为装饰类对象的接口类型对象
-
哪里好:会思考的人可能发现,这种模式其实可以用子类化的形式来进行,那到底优点在哪里呢
- 装饰类不会破坏原始类的性质,同时也添加不同的特性(这点子类化也能做到)
- 装饰类中的对象既可以是原始类对象,有也可以是其他装饰类对象,这样就能很方便的出现多种类型组合,而用子类化的方式去做到话,将会产生大量子类
- 哪里有: iOS中的范畴(Category)是一种变相的装饰者模式,他的原理是在编译的时候动态为原来的类添加方法,而不是拥有一个原始类的实例,严格意义上不是装饰者,但是却和装饰者思想很像,在需要添加的功能很少的时候可以用Category
4、责任链模式
-
结构图:
责任链 -
是什么:
- 责任链和装饰者很像,因为在实现统一接口的这些类中,都有一个自己接口的对象类型
- 但是又有所不同,装饰者是在统一的方法中添加新的功能,责任链则是都具有自己独一无二的功能
- 通过对象与对象之间的链接,构成一条完整的逻辑线
- 如同工厂里面的产品生产线,每个部门都有自己擅长的部分,只做自己的部分,其他部分交给其他部门来完成
-
哪里好:
- 将提交申请者和申请处理者之间的耦合解除,提交申请者不需要关心是谁在处理申请
- 处理者只需要关心自己擅长的部分,如果不会,则交给下一位去做
5、观察者模式
-
结构图:
观察者 -
是什么:
- 为对象之间添加一种依赖的关系,当某一方对象的属性发生变化的时候,另一方的对象自动进行相关更新操作
-
哪里好:
- 解除了相互依赖的对象之间过多的耦合,双方对对方的了解都很少
- 当一方对象属性变化时,另一方能自动做出相应的变化(这一点可以说是观察者模式的核心)
-
哪里有:iOS中有两种关于观察者模式的实现,一种是通知机制,一种是KVO机制,通知机制不是最原始的观察者模式,但是却在核心的思想上面进行适合iOS开发的修改,KVO能够很好诠释观察者模式
- 通知机制
- 使用了一个通知中心的单例来作为平台
- 被观察者为一个特定的类对象(NSNotification),该类的作用就是作为一种通知
- KVO
- 观察者实现一个方法(update方法),如果是是一个对象要使用,则封装在对象的.m文件中,如果是视图控制器使用,贼在试图控制器中实现就好
- 被观察者需要添加观察者,一般来说我们会添加self,然后在当前控制器上实现方法,可能很多人都会使用这个,但是却不了解这个原理,这一步的操作实际上是将该控制器对象作为观察者添加了,实现的这个方法,其实就是观察者的update方法
- 关于怎么在属性变化的时候通知观察者调用update方法,这个逻辑被封装到了KVO内部,自己也能试着模拟
- 通知机制
6、命令模式
-
结构图:
命令 -
是什么:
- 对于直接调用的方法,利用一个对象,将方法封装起来,被称为命令
- 并通过一个类似于“服务员”的角色和调用命令的对象进行交互
-
哪里好:命令模式从本质上来说,其实就是将直接调用的方法封装起来,变成一个对象,好处有这些
- 封装起来,相当于是一种延时操作,所以可以利用这种模式做撤销
- 同时可以为方法设置一个队列,具有执行顺序的队列,在什么时候执行也可以加以限制
-
哪里有:
- NSInvocation
-
结构图:
NSInvocation - NSInvocation的角色就是命令模式中的封装方法的命令类
-
结构图:
- NSUndoManager
- NSUndoManager提供了撤销机制
- NSUndoManger也是利用了NSInvocation类对象作为命令角色
- NSInvocation
7、备忘录模式
-
结构图:
备忘录 - 是什么:
- 捕获对象内部的状态,在对象之外封装起来,方便恢复
-
哪里好:
- 备忘录模式在很多场景都会使用,例如存档,游戏进度存储和读取等等
-
哪里有:
- 归档
- 备忘录对象即为归档对象
- 被归档的对象为操作对象
- 文件系统则为备忘录管理者
- 属性列表序列化
- 归档
8、组合模式
-
结构图:
组合 -
是什么:
- 将同一类对象组合成树状结构,具有部分和整体的层次,但是部分和整体保持一致性
-
哪里好:
- 能够保证整体和单个的一致性,对外界的接口是一致的
- 对整体的处理能够遍历到所有局部个体
-
哪里有:
- 在UIKit框架中,UIView的设计能够非常明确地体现这种设计模式
- UIView是可以添加UIView类以及它子类的对象的
- UIView对象的渲染以及其他操作会遍历到子类去依次进行
-
图示:
UIView如何体现组合模式
- 在UIKit框架中,UIView的设计能够非常明确地体现这种设计模式
9、单例模式
-
结构图:
单利 -
是什么:相信单例模式是iOS开发者非常熟悉的几个模式之一
- 保证一个类仅有一个实例
- 同时提供一个访问该唯一实例的访问接口
-
哪里好:
- 当一个类只需要产生一个实例的时候会用到单例模式
-
哪里有:相信iOS开发者对一些存在于开发中的单例类已经非常熟悉
- UIApplication:控制iOS应用程序的类
- UIAccelerometer:加速器感应的类
- NSFileManager:文件管理的类
最后,还需要推荐一篇文章,是我在前年写的,现在看起来,还是蛮不错的一篇文章iOS单例设计模式详细讲解(单例设计模式不断完善的过程)
10、策略模式
-
结构图:
策略 -
是什么:
- 定义一系列的算法,将他们封装为对象,可以相互替换
-
哪里好:
- 策略模式的作用就是将大量的if else结构给除去,使用对象来执行操作
- 避免将算法的具体细节暴露给外部
-
哪里有:
- MVC模式中涵盖了很多其他的模式,其中策略模式就是其中之一,策略模式在MVC模式中的体现为:控制器作为视图的策略类,视图在没有控制器的情况下,显示应该是一样的,但是不同的控制器将会给视图赋予不同的数据已经输出模式等
11、原型模式
-
结构图:
原型 -
是什么:
- 为一个类添加复制操作来创建新的对象
-
哪里好:
- 如果一个对象是组合对象(见组合模式),那么使用原型模式来为对象创建克隆方法较为方便
-
哪里有:原型模式在实际开发中使用的并不是很多,但是原型模式中克隆方法就有所讲究了,这里对克隆的方法简要说明一下:
- 浅复制:
- 只复制指向实际对象的指针,两个指针指向的仍然是一个资源
- 浅复制
- 深复制:
- 复制指向对象的指针以及指针指向的资源
- 深复制
- 浅复制:
具体iOS中的体现,跳转iOS剖析深浅复制
12、生成器模式
-
结构图:
生成器 -
是什么:
- 将创建对象的细节封装到其他类
- 将多样化的创建过程封装起来,用户只需要制定创建的类型即可
-
哪里好:
- 客户类中的创建代码不再需要大量if else进行判断创建,直接制定类型即可创建
- 将创建过程封装起来,变得非常稳定不易出错
-
和抽象工厂有什么区别:
区别
13、代理模式
-
结构图:
代理 -
是什么:
- 一个类委托另外一个类去做一件事情
-
哪里有:
- iOS中的协议、代理和委托对象构成了代理模式:
- 协议:是约束代理对象需要进行的操作
- 代理:执行被指派的操作
- 委托:指派任务的角色
- 用UITableView来细化这种模式:
- UITableView:作为委托者,内部拥有一个代理对象,在创建视图的方法中(推测内部实现)会使用代理对象执行协议中的方法
- UITableViewDelegate&UITableViewDataSource:作为协议,约束了代理对象需要实现的方法
- UIViewController(或者是为了系统瘦身自定定义的一个代理类):实现了协议中的方法
- iOS中的协议、代理和委托对象构成了代理模式:
14、适配模式
-
结构图:
-
类适配模式:
类适配器 -
对象适配器模式:
对象适配器
-
-
是什么:
- 一个类想要调用另外一个类的方法,但是却不兼容
- 利用适配器类包装不兼容的方法,变为兼容方法供其他类使用
-
哪里好:
- 在方法调用不兼容的时候使用
-
哪里有:
- iOS开发中如果导入了某个SDK,如果SDK中有一些方法在你的APP中并不兼容,那么这个时候适配器模式就能大显神威
- 利用协议为适配器类约定方法
- 将SDK中方法封装到协议方法中
- 并添加上自己APP中需要进行的处理
- iOS开发中如果导入了某个SDK,如果SDK中有一些方法在你的APP中并不兼容,那么这个时候适配器模式就能大显神威
15、模板方法模式
-
结构图:
模板方法 -
是什么:
- 利用一个抽象类封装基本的方法,并提供一些需要让子类去完善的方法接口
- 在基本方法中调用这些需要让子类完善的方法,从而达到不同的子类可以根据不同的情况实现不同的代码
-
哪里有:
- UIView的drawRect方法:该方法是用于描述UIView长什么样子的,这个方法留给子类去实现,从而实现不同绘制
- UIViewController的设备不同方向的方法
16、迭代器模式
-
结构图:
迭代器 -
是什么:
- 迭代器类中拥有需要遍历的集合的对象,在暴露出来的方法中操作集合对象
-
哪里好:
- 如果不想暴露集合内部的信息,那么可以使用迭代器模式,通过迭代器暴露的方法来对集合进行访问和操作
-
哪里有:
- NSEnumerator是iOS中的迭代器类,可以对常用的集合对象进行访问和操作
17、桥接模式
-
结构图:
桥接
-
是什么:
- 很多书上说:抽象部分和实现部分分离,但是我觉得这种解释很不容易理解
- 这里给出通俗的解释:相互关联的类中,每个类都具有类的抽象,同时也有具体类的实现,这种情况下,与其将两个类体系利用继承融合起来,还不如使用聚合/组合将两个类体现关联起来。
-
哪里好:
- 两个类体系对外界暴露的接口都是抽象的,所以修改一个类体系的类的具体实现是不会对另一个类体系造成影响的
- 可以有很多种组合方法,到底是A中拥有B,还是B中拥有A,都是可以实现的
18、外观模式
-
结构图:
外观 -
是什么:
- 为一组具有关联的接口提供一个统一的接口,简化调用
-
哪里好:
- 避免了必须知道一系列接口的调用细节才能完成功能的尴尬情况