创建型设计模式
创建型设计模式通常是五种。简单工厂模式是工厂模式的简化,使用频繁,所以单独拿出来。
| 模式名称 | 核心思想 | 典型应用场景 | iOS示例 | 优缺点对比 |
|---|---|---|---|---|
| 单例 (Singleton) | 确保类只有一个实例并提供全局访问点 | 全局配置、共享服务 |
[UIApplication sharedApplication]、[NSNotificationCenter defaultCenter]
|
✅ 节约资源 ❌ 测试困难,可能隐藏依赖 |
| 工厂方法 (Factory Method) | 定义创建对象的接口,让子类决定实例化哪个类 | 多平台UI组件创建 |
[NSNumber numberWith...]系列方法、UIFont的+systemFontOfSize:
|
✅ 解耦调用方与具体类 ❌ 每新增产品需新增工厂类 |
| 抽象工厂 (Abstract Factory) | 创建相关或依赖对象的家族,而不需指定具体类 | 跨平台UI套件、主题系统 |
NSArray/NSMutableArray的alloc/init组合 |
✅ 保证产品兼容性 ❌ 扩展新产品族困难 |
| 建造者 (Builder) | 分步骤创建复杂对象,相同构建过程可生成不同表示 | 复杂对象配置 |
UIAlertController配置、NSURLComponents
|
✅ 灵活构造对象 ❌ 代码冗余,适合复杂对象 |
| 原型 (Prototype) | 通过复制现有对象来创建新对象 | 对象创建成本高时 |
NSCopying协议(copy方法)、UIView的copyWithZone:
|
✅ 避免重复初始化 ❌ 深拷贝实现复杂 |
关键对比维度:
- 灵活性:建造者 > 工厂方法 > 单例
- 复杂度:抽象工厂 > 建造者 > 原型
- iOS集成度:单例 > 工厂方法 > 原型
-
适用场景:
- 简单全局访问 → 单例
- 多态对象创建 → 工厂方法
- 复杂参数配置 → 建造者
零.简单工厂模式
简单工厂模式提供了一个创建对象的接口,根据传入参数决定返回实例。
应用
比如苹果系统里:
[UIImage imageNamed:"icon"];
[UIImage imageWithContentsOfFile:path];
优点:将对象的创建使用分离,减低耦合。
缺点:新增产品类的时候,会改动工厂的创建细节,违反了开闭原则。
Apple使用的基本都是简单工厂模式,更简洁简单。
一.工厂方法模式
工厂方法模式将对象的实例化交给子类实现,通过父类方法调用,子类决定返回哪个对象。(抽象工厂类+多个具体工厂类+多个产品类)
比如苹果系统里的NSCoder,具体提供了NSKeyedArchiver以及NSArchiver等几种方式编码解码。
比如cellForRow,可以返回不同的Cell.
典型第三方应用:CTMediator
通过字符串+消息转发的方式,将vc的创建交给各自的模块,统一返回uiviewcontroller。体现工厂方法的核心精神:延迟决定具体类型,对客户隐藏创建细节,便于扩展。
(根据target找到对应的类,实例化对象,调用其action方法。每个模块都有自己的target类和action方法的具体实现。)
不完全是工厂模式的原因,没有使用传统的protocol定义接口,而是使用target+action的字符串映射方式。
实际应用:
比如点击一个按钮,可能根据逻辑的不同创建不同的子VC。子类封装详细的创建过程,决定返回哪个对象。而对外是统一的viewController。
OC强大的runtime,分类和block常用来解耦。所以更多的是简单工厂模式。纯粹的工厂方法模式需要额外的子类,优点重,开源库里并不多。
二.抽象工厂模式
创建一系列相关或者相互依赖对象的接口,无需指定具体的类。(满足开闭原则,对扩展开放,对修改关闭)
比如苹果系统里:
Apple提供了一个抽象的外功工厂机制,让我们可以根据不同环境统一创建,配置一整套控件外观。
抽象工厂:UIAppearance。
具体工厂:UITraitCollection
抽象产品: 遵循的控件
具体产品:UILabel,UIButton
核心:统一生产/配置一整套互相关联的UI样式对象。
通过UIAppearance的接口操作各个具体控件。
三.单例模式
确保一个类只有一个实例,并提供一个全局访问点。适用于全局唯一,统一管理,系统服务类。
苹果系统里:
全局状态共享:UserDefaults,UIApplication
系统统一管理:NotificationCenter,URLSession
资源复用,避免重复实例化:FileManager,AVAudioSession
优点:不用重复创建,任何地点访问。
缺点:滥用,内存升高,错误修改,可能耦合度高,测试困难。
由于存在经典问题:
优化一:依赖注入
将单例变为普通实例,需要的地方创建单例并通过依赖注意方式(指定满足某个协议,该单例实现了此协议)传入,然后在内部使用。
四.建造者模式
将复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
优点:分不构造复杂对象,更好的封装性
缺点:多个类,代码复杂
苹果系统:
UIAlertController的使用。
分步创建title,actions,最后present出去。
实际应用:
比如项目的各种配置,比如多段动画的组合。
原型模式
通过复制现有的对象来创建新对象,而不是通过传统的新建类实例的方式。在 iOS 开发中,原型模式主要通过 NSCopying 协议实现。
优点:避免复杂初始化过程,更高效。
缺点:需要注意深拷贝与浅拷贝。
实际使用:
比如富文本模式。可以创建一个消息模板,指定颜色字体等等。后续只需要copy再修改文字即可,避免初始化代价高。
结构型设计模式
| 模式名称 | 核心思想 | 典型应用场景 | iOS示例 | 优缺点对比 |
|---|---|---|---|---|
| 适配器 (Adapter) | 转换接口使不兼容的类可以协作 | 集成第三方库、旧系统改造 |
UITableViewDataSource、NSURLSession与Alamofire桥接 |
✅ 解决接口兼容问题 ❌ 过度使用会增加复杂度 |
| 桥接 (Bridge) | 将抽象与实现分离,使它们可以独立变化 | 跨平台UI渲染、多数据库支持 |
UISwitch/UISlider的样式与逻辑分离、Core Graphics绘图API |
✅ 提高扩展性 ❌ 设计难度较高 |
| 组合 (Composite) | 以树形结构组合对象,统一对待单个对象和组合对象 | 视图层级、菜单系统 |
UIView的addSubview:、UIStackView
|
✅ 简化客户端代码 ❌ 类型检查可能变复杂 |
| 装饰器 (Decorator) | 动态添加职责而不修改原类 | 扩展对象功能 |
Category扩展方法、CALayer视觉装饰 |
✅ 比继承更灵活 ❌ 多层装饰难调试 |
| 外观 (Facade) | 为复杂子系统提供统一简化接口 | SDK封装、复杂系统简化 |
AVFoundation的AVPlayer封装底层音视频处理 |
✅ 降低使用难度 ❌ 可能限制灵活性 |
| 享元 (Flyweight) | 共享细粒度对象以减少内存占用 | 频繁创建的对象池化 |
UITableViewCell重用机制、UIColor共享对象 |
✅ 提升性能 ❌ 需要区分内部/外部状态 |
| 代理 (Proxy) | 为其他对象提供代理以控制访问 | 懒加载、访问控制 |
NSProxy、UIScrollViewDelegate(部分特性) |
✅ 增强安全性/功能 ❌ 可能引入额外调用开销 |
关键对比维度:
- 接口转换能力:适配器 > 外观 > 代理
- 结构灵活性:组合 > 桥接 > 装饰器
- 性能优化:享元 > 代理 > 外观
- iOS集成度:装饰器(Category) > 代理(Delegate) > 组合(UIView)
使用建议:
-
接口兼容 →
Protocol+适配器类(适配器) -
多维度扩展 →
Category+组合对象(装饰器) - 复杂系统封装 → 统一管理器类(外观)
- 性能敏感场景 → 对象池/缓存(享元)
一.代理模式
代理模式是一种对象之间一对一通信方式,一个对象将某些事情的决策权、执行权交给另一个对象去完成。
优点:解耦,灵活,职责清晰。
典型的代表就是UITableViewDelegate,UITableViewDataSource
二.适配器模式
用于将一个类的接口转换成客户端期望的另一种接口,让原本不兼容的两个类可以协同工作。
比如:
对已有的老的不兼容的就旧接口做统一适配,包装成新接口,就是适配器模式。
典型:
不同平台登陆适配,不同平台支付适配。swift与OC代码桥接。
典型第三方应用:AFNetworking
AFNetworking中,AFURLSessionManager对NSURLSession的代理方法进行了适配,转换成了block的回调方式,并且统一管理task与回调的关联
典型适配器,把代理适配成了block
原接口:NSURLSession代理方法(delegate)
目标接口:block回调形式
三.外观模式
为复杂子系统提供一个统一的、高层的对外接口,让外部代码不需要直接依赖子系统的复杂实现,达到简化调用、解耦依赖的目的。
(简单说:为复杂的内部系统提供一个门面)
系统应用:
UIApplication就是系统的门面,包含时间分发,状态窗口管理等等。
实际应用:
将项目作为SDK提供给客户使用时,统一提供Interface类供客户使用。简化使用,隐藏内部复杂细节,是典型的外观模式。
AFNetworking中使用NSURLSessionManager对NSURLSession的封装体现了适配器设计模式的使用。而使用AFHTTPSessionManager统一封装了请求,序列化,错误处理等子系统,提供了简化统一的对外接口,则体现了外观模式的设计模式。
四.桥接模式
将抽象部分与实现部分分离,是他们可以独立变化,通过桥接类再它们之间建立连接。方便替换。
系统应用:
UIView和CAlayer。
UIView是抽象层,负责响应,布局,事件等。CALayer是实现层,负责绘制,动画等。
UIView内部组合CALayer,形成灵活的桥接结构。
举例:
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
//对shapeLayer进行各种操作
...
...
UIView *customView = [[UIView alloc] initWithFrame:frame];
[customView.layer addSublayer:shapeLayer];
view和layer独立实现,灵活组合。
典型第三方应用:AFNetworking
AFNetworkging的抽象层是AFURLSessionManager,供上层使用。具体的实现层是NSURLSession。通过delegate和block的方式桥接二者。
典型第三方应用:SDWebimage
// 运行时注册不同解码器
[[SDImageCodersManager sharedManager] addCoder:[SDWebImageJPEGCoder new]];
// 注册使用不同的imageloader
SDWebImageManager.sharedManager.loaders = @[
[SDImageCache sharedCache],
[MyCustomLoader new],
[SDWebImageDownloader sharedDownloader]
];
五.组合模式
将对象组合成树形结构以表示“部分-整体”的层次结构,使客户端对单个对象和对象集合的使用具有一致性。
(将单个对象和组合对象统一对待)
系统应用:
UIView的视图层级系统。UIView既可以是单个对象(比如一个UILabel),也可以是对象集合(比如嵌套多层的根view),都统一对外提供addSubview,removeFromSuperview等接口。
优点:清晰表达了部分-整体结构。无需区分单个对象和组合对象,统一操作
缺点:结构嵌套多不易维护
典型第三方应用:Masonary
Masonary把每个MASConstraint统一放到MASConstraintMaker中,统一使用make处理。
六.享元模式
共享相同对象,减少内存使用,提高性能。核心思想是把对象分为内部状态和外部状态,内部状态共享不变(存在共享池),在外部环境中变化的部分,由外部传入(使用的具体内容,场景等)。
系统应用:
UIFont就是典型的享元模式。多个地方使用相同的字体,系统只保留一份UIFont对象,避免重复开销。
比如使用UIFont.systemFont(ofSize:14),系统会先查找字体缓存池,如果有则直接返回,如果没有则创建新的并加入。
tableviewCell的重用也是享元模式的一种具体应用。
典型第三方应用:SDWebimage
SDWebimage中的SDImageCache就是一个享元对象池,合理复用相同的图片资源,提升性能。
七.装饰器模式
在不改变原有对象结构的前提下,通过装饰器动态包装,动态地为其增加额外的功能。
系统应用:
UIControl通过状态调整外观,有些类似装饰器模式。但这些状态管理机制是通过配置,合肥包装,所以有区别。
典型第三方应用:CocoaLumberjack
设置不同的日志目标来装饰日志记录行为:
//控制台输出日志
[DDLog addLogger:[DDTTYLogger sharedInstance]];
//文件输出日志
[DDLog addLogger:[[DDFileLogger alloc] init]];
//设置日志级别
[DDTTYLogger sharedInstance].logFormatter = [[CustomLogFormatter alloc] init];
CocoaLumberjack在设计时大量使用了装饰器模式,尤其在日志目标和日志级管理上。
日志目标作为装饰器:通过组合多个日志目标,动态添加日志输出目标,不改变记录日志核心内容
日志级别控制:日志级别的装饰,可以根据配置过滤日志,控制哪些日志被记录,进一步增强了装饰器模式的灵活性。
行为型设计模式
总结对比表
| 模式名称 | 核心思想 | 典型应用场景 | iOS示例 | 优缺点对比 |
|---|---|---|---|---|
| 责任链 (Chain of Responsibility) | 多个对象依次处理请求,直到有对象处理它为止 | 事件传递、审批流程 |
UIResponder链(nextResponder)、NSURLProtocol拦截 |
✅ 降低耦合度 ❌ 请求可能未被处理 |
| 命令 (Command) | 将请求封装为对象,支持参数化、队列化和日志化 | 撤销/重做、远程控制 |
NSInvocation、UIButton的addTarget:action:
|
✅ 解耦调用者和执行者 ❌ 可能产生大量命令类 |
| 解释器 (Interpreter) | 定义语法规则并解释语言中的句子 | 正则表达式、DSL语言 |
NSPredicate、NSExpression
|
✅ 易于扩展语法 ❌ 复杂语法难以维护 |
| 迭代器 (Iterator) | 提供顺序访问聚合对象元素的方法 | 集合遍历 |
NSEnumerator、快速枚举(for...in) |
✅ 统一遍历接口 ❌ 某些语言已内置支持 |
| 中介者 (Mediator) | 通过中介对象封装一组对象的交互 | 复杂UI组件通信 |
UIDataSourceModelAssociation、自定义协调器 |
✅ 减少对象间依赖 ❌ 中介类可能变得复杂 |
| 备忘录 (Memento) | 捕获并外部化对象状态以便后续恢复 | 撤销操作、状态持久化 |
NSKeyedArchiver、UIStateRestoring
|
✅ 状态保存/恢复简单 ❌ 可能消耗内存 |
| 观察者 (Observer) | 对象间定义一对多依赖,状态变化时自动通知 | 数据绑定、事件通知 |
KVO、NSNotificationCenter、Combine框架 |
✅ 实时响应变化 ❌ 需注意注销观察者 |
| 状态 (State) | 允许对象在内部状态改变时改变行为 | 订单状态机、UI控件状态 |
UIView的isHidden/isUserInteractionEnabled、UIControl状态 |
✅ 简化条件分支 ❌ 状态多时代码量增加 |
| 策略 (Strategy) | 定义算法族并使其可互换 | 排序算法、布局策略 |
NSSortDescriptor、UICollectionViewLayout
|
✅ 灵活切换算法 ❌ 客户端需了解策略差异 |
| 模板方法 (Template Method) | 在父类定义算法骨架,子类重写特定步骤 | 生命周期钩子、流程标准化 |
UIViewController生命周期方法、NSOperation的main方法 |
✅ 代码复用 ❌ 可能违反开闭原则 |
| 访问者 (Visitor) | 将操作与对象结构分离,可在不修改类的情况下新增操作 | 复杂数据结构操作 |
NSFileManager的目录遍历、自定义AST处理 |
✅ 集中相关操作 ❌ 增加新元素类困难 |
关键对比维度:
- 解耦程度:观察者 > 中介者 > 命令
- 扩展性:访问者 > 策略 > 状态
- iOS集成度:观察者(KVO) > 模板方法(生命周期) > 责任链(Responder Chain)
- 复杂度:访问者 > 解释器 > 中介者
使用建议:
-
简单事件通知 →
NSNotificationCenter(观察者) -
算法切换 →
NSSortDescriptor(策略) -
UI状态管理 →
UIControlState(状态) -
撤销操作 →
NSUndoManager(命令+备忘录)
一.观察者模式
一种一对多的依赖关系。让多个观察者对象同时监听某一个主题对象。当主题对象发生改变时,它会自动通知所有依赖于它的观察者对象,使它们能够及时更新。
系统应用:
NotificationCenter 通知中心
KVO
优点:解耦,灵活,多对多。
缺点:通知链复杂,不易维护
典型第三方应用:ReactiveObjC
ReactiveObjC就是用信号+订阅者的方式实现了观察者模式。它同时做了链式、响应式、异步流的封装,写起来更优雅灵活。
二.访问者模式
在不改变数据结构的前提下,给一组对象增加新的操作。通过定义访问者,封装对一组元素对象的操作,把数据结构和操作解耦。
比如:公园中有草地,花坛,长椅等元素。不同的访问者执行不同的操作。清洁工打扫草地,园艺师修剪花坛,检察员检查长椅。
系统应用:
遍历view树,为其中的每一个控件(UIButton,UILabel)等加边框,或者检查状态,进行统计等。
典型第三方应用:SDWebImage
其中的清理内存操作用到了访问者思想,但由于缓存只有磁盘和内存两种,并没有设计具体的visiter。
//遍历所有缓存文件
//判断缓存文件最后的修改日期是否过期
//如果过期则删除
虽然访问者模式本身是个比较结构话的设计模式,但很多优秀第三方库在对象遍历+操作解耦产经,都在用类似访问者模式的思想,只是不完全一样。
三.中介者模式
用于减少对象之间直接交互,避免多个对象直接互相引用,减少它们之间的依赖。中介者模式通过一个中介对象来协调对象间的交互,从而降低它们的耦合度。
系统应用:NotificationCenter
传统面向对象中对象间直接互相调用,紧耦合。而NotificationCenter提供了一种松耦合机制,允许对象间通过NotificationCenter这个中介者进行发布通知和接受通知,无需直接联系。
典型第三方应用:CocoaLumberjack
DDlog作为中介者,接受来自不同组件的信息。这些组件之间不直接进行通信。
四.命令模式
将请求封装成对象,从而使你可以用不同的请求、队列、日志来参数化其他对象,并支持撤销、重做、事务等操作。
系统应用:
UIButton的Target-Action就是典型的命令模式。
Button(invoker)触发事件,调用对应的action(command),执行到target(receiver)。
(命令模式的简化,如果扩展为命令对象+历史记录,就能支持撤销,重做,完美实现完整的命令模式)
典型第三方应用:AFNetworking
AFHTTPRequestOperationManager内部维护一个operationQueue为调用者
AFURLConnectionOperation为命令,实现了start及cancel的处理
NSURLSessionTask为具体的接收者
典型第三方应用:SDWebImage
SDWebImageDownloader维护队列是invoker
SDWebImageDownloadOperation是命令对象,封装了开始(start),取消(cancel)等逻辑
NSURLSession
命令模式等于把请求封装成命令对象,调用者执行命令,接收者处理具体逻辑。解耦调用者和接收者,便于拓展、撤销、重做等。
五.状态模式
润许一个对象在内部状态改变时,行为也改变,就像变成另一个对象一样。
系统应用:
UIButton的Normal,Highlighted,Disabled,Selected。不同的状态显示的文字图片颜色等都不一样。是状态模式的一种应用。
典型第三方应用:MJRefresh
根据当前不同的状态(空闲,下拉,刷新,没有更多数据),显示不同的UI,执行不同的逻辑。
状态模式通过封装状态、分离行为,让对象在内部状态变化时,自动切换行为,避免了复杂的条件分支,让代码更优雅。iOS中的UI控件状态、任务状态、播放器状态等都用到了这种设计模式。
六.策略模式
定义一系列算法,封装起来,并且使它们可以相互替换。
系统应用:
NSURLSesssion的请求缓存策略。配置不同的策略,在发起请求时根据不同的策略执行不同的缓存规则。
UIViewContentMode。根据多种不同的策略,决定子视图内容如何在父视图中进行排布和缩放。
典型第三方应用:SDWebimage
SDWebimage的缓存策略。
SDWebiamge的解码策略。
SDWebimage的setimage策略。注意这不是严格意义上的策略模式,而是策略配置。使用枚举+分支的判断方式,控制不同策略路径。但没有抽象出统一策略接口+多个独立策略类。
iOS系统中大量使用了策略模式,通过定义统一策略接口(枚举、协议、Block)+多个具体实现策略+context环境类,达成运行时可自由切换、行为解耦、扩展灵活的优雅设计。
七.解释器模式
主要是将语言中的句子转化为能够被计算机理解的格式。
系统应用:
正则表达式
NSPredicate(谓词)
八.迭代器模式
为一个集合(数组、列表、集合)提供一种顺序访问其元素的方法,而不暴露内部表示。通过迭代器模式,客户端可以以统一的方式遍历集合元素,且不需要关心集合内部的具体实现。
系统应用:NSArray
NSArray的enumerateObjectsUsingBlock方法,就是允许我们使用迭代器的方式来遍历数组。
同样可以使用迭代器来遍历缓存。
九. 备忘录模式
允许在不暴露对象内部结构的情况夏,保存和恢复对象的状态。
系统应用:UItextView
在文本编辑应用中,每次用户编辑文本时,程序会保存当前的文本状态,并允许用户在需要时恢复到先前的状态。
典型第三方应用:Realm
十. 模板方法
父类定义好模板方法,规定好步骤顺序,子类通过重写某些方法,来改变某些细节步骤。
系统应用:UITableview
整个UITableView的流程时苹果写好的,而我们可以定制它流程中的某些步骤。
典型第三方应用:AFNetworking
父类模板:AFURLSessionManager类封装了NSURLSession的网络请求管理,定义了表组合你的网络请求-相应处理流程。包括:创建task->监听task回调->解析响应->处理错误->执行完成回调。
子类重写:比如服务器给我们数据加密,我们可以在URLSession: task: didCompleteWithError中先进行手动解密,再走后续解析(task.completionHandler()),流程顺序不变,细节改变。
子类重写:比如自定义下载进度上报方式。我们可以在URLSession:dataTask:didReceiveData中重写,每5%回调一次。
十一.责任链模式
将多个处理者串联成一条链,请求从链起点对象发出,依次沿着链传递,直到有一个对象处理它为止。
系统应用:UIResponder事件响应链
iOS中的UIResponder链,比如触摸事件touchesBegan:withEvent:就是典型的责任链模式。如果当前视图不处理,事件就会沿着nextResponder传递,直到有对象处理。