苹果开发者设计模式与开发范式
这是我参与「第四届青训营 -IOS场」笔记创作活动的第5篇笔记
设计模式 Design Pattern
设计模式最重要的四大要素:
- 模式名称 (Pattern Name): 通过几个词简要的描述了模式的问题、解决方案和效果。
- 问题 (Problem):描述了该在何时使用此设计模式,其中包含了原本设计中存在的问题和问题存在的原因。
- 解决方案 (solution):描述了设计模式的组成成分,以及成分之间的相互关系、各自的职责和协作方式。通常用UML类图来表达。
- 效果 (Consequences):描述了设计模式的优缺点以及在使用模式时需要权衡的问题。
三大类:
- 创建型(Creational Pattern):用于描述如何创建对象
- 结构型(Structural Pattern):用于描述如何实现多个类或对象的组合
- 行为型(Behavioral Pattern):用于描述类或对象如何交互以及如何分配职责
IOS 中最常见的三个设计模式
代理模式 Delegate Pattern
- DelegatenInterface 代理协议: 即被代理人必须满足的协议
- Deleage 被代理人: 遵循代理协议,具体实现代理协议里所有要求的方法
- Delegator 代理人: 拥有(多个)被代理人的引用,可以向代理人发起任务代理,无需知道代理人具体是如何完成任务的。
四要素:
- 模式名称:Delegate
- 问题:解耦当前功能不关心的逻辑
- 解决方案:依赖协议,代理方实现
- 效果:可以复用基础组件
例子:
protocol AdvancedLifeSupport{
var handler: EmergencyCallHandler { get set }
func performCPR()
}
class EmergencyCallHandler {
var delegate: AdvancedLifeSupport?
func assessSituation(){print("What was the emergency?")}
func medicalEmergency(){delegate?.performCPR()}
}
struct Paramedic: AdvancedLifeSupport {
var handler: EmergencyCallHandler
init(handler: EmergencyCallHandler){
self.handler = handler
self.handler.delegate = self
}
func performCPR(){
print("The paramedic does chest compressions, 30 per second.")
}
}
class Doctor : AdvancedLifeSupport {
var handler: EmergencyCallHandler
init(handler: EmergencyCallHandler){
self.handler = handler
self.handler.delegate = self
}
func performCPR(){
print("The paramedic does chest compressions, 30 per second.")
print("Listening for heart sounds")
}
}
//usage
let tom = EmergencyCallHandler()
let pete = Paramedic(handler: tom)
tom.assessSituation()
tom.medicalEmergency()
在上面的例子中,我们有一个类, 紧急呼叫接听员(EmergencyCallHandle),他接到急求电话(assessSituation) 后无法亲自去现场处理。那么他的解决方法就是找拥有急救资质(AdvancedLifeSupport)的人代理。在这种情形下,接听员不需要知道被代理人是谁(护理人员Paramedic? 或者是医生 Doctor?), 和如何处理急救的(不需要知道代理方的 performCPR的实现), 只需要保证被代理人有急救资质就可以了。
- 被代理人必须拥有特定资质(即遵循 delegate协议)
- 被代理人初始化后需要向代理发起者注册他的资料,这样代理发起者才能向被代理人发起任务代理。
在IOS 编程中的代理模式
在 Cocoa 框架内, 代理模式的使用场景非常多,多用于解耦组件, 例如UITableView, 他需要一个UItableViewDelegate 来处理UI交互,并需要一个UItalbeViewDatasource 来提供数据。
抽象工厂 AbstrctFactory
图引用自:# iOS 客户端专场 学习资料四第四届字节跳动青训营
- AbstractFactory 抽象工厂:声明了一组用于创建一族产品的方法,每一个方法对应一种产品。
- ConcreteFactory 具体工厂:实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。
- AbstractProduct 抽象产品:为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法。
- ConcreteProduct 具体产品:定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的业务方法。
例子:
//产品1: 可乐的抽象类与派生类
@interface Cola: NSobject //抽象产品
@end
@interface CocaCola: Cola //具体产品
@end
@interface PesiCola: Cola //具体产品
@end
//产品2: 瓶子的抽象类和派生类
@interface Bottle: NSobject //抽象产品
@end
@interface CocaColaBottle: Bottle //具体产品
@end
@interface PesiColaBottle: Bottle //具体产品
@end
//工厂抽象类
@implementation ColaFactory
+ (Cola *) createCola{ return [Cola new]; }
+ (Bottle *) createBottle{ return [Bottle new]; }
@end
//可口可乐主题工厂 - 具体工厂
@implementation CocaColaFactory
+ (CocaCola *) createCola{ return [CocaCola new]; }
+ (CocaColaBottle *) createBottle{ return [CocaColaBottle new]; }
@end
//百事可乐主题工厂 - 具体工厂
@implementation CocaColaFactory
+ (PesiCola *) createCola{ return [PesiCola new]; }
+ (PesiColaBottle *) createBottle{ return [PesiColaBottle new]; }
@end
//可口可乐产品线
Cola * cocaCola = [CocaColaFactory createCola];
ColaBottle * cocaColaBottle = [CocaColaFactory createBottle];
//百事可乐产品线
Cola * pasiCola = [PasiColaFactory createCola];
ColaBottle * pasiColaBottle = [PasiColaFactory createBottle];
抽象工厂模式的优缺点:
- 优点:
- 隔离了具体产品类的生成,用户如果只是通过抽象类的公共方法来获取产品,这种情况下切换产品工厂很容易。
- 可以保证来自于同一个具体工厂的不同等级的产品属于同一产品族,比如可口可乐工厂生产的所有产品(可乐,瓶子等)都是可口可乐主题的。
- 容易增加新产品族,比如我们想增加一个崂山可乐的产品族,只需要创建新的相对应的具体工厂即可,无需对现有抽象工厂和具体工厂的代码做任何更改。
- 缺点:
- 难以增加新的产品等级(新的产品类),比如我们想工厂还可以生产相应主题的包装盒,那我们得在先声明新的抽象产品类和所有相对应的实体产品类(
ColaPackge, CocaColaPackage, PasiColaPackage),抽象工厂类添加新的抽象方法createColaPackage,以及给每个实体工厂加上抽象方法的实现。如果我们的具体工厂种类数量很大,那么需要添加的代码将极其庞大。
- 难以增加新的产品等级(新的产品类),比如我们想工厂还可以生产相应主题的包装盒,那我们得在先声明新的抽象产品类和所有相对应的实体产品类(
因此抽象工厂模式常用于产品等级结构稳定的情况。
在IOS 编程中的抽象工厂模式
在 Foundation framework框架下, 有一个设计模式 类簇 Class Cluster,他用于管理一组隐藏在公共抽象父类下的具体私有类。与抽象工厂模式有一定的相似性。
比如 OC 中有一个抽象父类 NSNumber, 他拥有多个具体子类NSChar, NSInt, NSFloat, NSDouble。NSNumber在其方法中声明了子类共有的操作,比如都可以转换成字符串,都可以子类间互相转换。
具体的实现方法就是 NSNumber不会声明一个实例变量来存储不同类型的数据,而是由其子类创建对应类型的实例变量并将调用接口共享给抽象父类NSNubmer,就像像可乐例子中 抽象父类Cola 有抽象方法createCola,而每个具体子类有他们不同的具体实现。
类簇拥有和抽象工厂模式相同的优缺点:容易增加新的具体子类,难以给现有子类扩展新功能。
观察者模式 Observer Pattern
- Subject 目标:被观察的对象。在目标中定义了一个观察者集合,一个观察目标可以接受任意数量的观察者来观察,它提供一系列方法来增加和删除观察者对象,同时它定义了通知方法notify()。目标类可以是接口,也可以是抽象类或具体类。
- ConcreteSubject 具体目标:目标类的子类,用于扩展目标类,它包含有经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知;实现了父类目标类定义的抽象方法。
- Observer 观察者:观察者将对观察目标的改变做出反应,观察者一般定义为接口,该接口声明了更新数据的方法update(),因此又称为抽象观察者。
- ConcreteObserver具体观察者:在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致;它实现了在抽象观察者Observer中定义的update()方法。
观察者模式包含观察目标和观察者两类对象,一个目标可以有任意数目的与之相依赖的观察者,一旦观察目标的状态发生改变,所有的观察者都将得到通知。作为对这个通知的响应,每个观察者都将监视观察目标的状态以使其状态与目标状态同步,这种交互也称为发布-订阅(Publish-Subscribe)。观察目标是通知的发布者,它发出通知时并不需要知道谁是它的观察者,可以有任意数目的观察者订阅它并接收通知。
观察者模式的优缺点:
- 优点:
- 实现了UI表示层和数据逻辑层的分离
- 在观察目标和观察者之间建立了抽象的耦合,双方无需知道对方的细节。
- 支持广播通信。
- 满足开闭原则,容易增加新的具体观察者和给观察者添加新的观察目标。
- 缺点
- 如果一组观察者和目标之间有多个间接观察者,那么将信息传递给所有观察者会花费很多时间。
- 如果观察者和目标之间产生依赖循环,会导致系统崩溃
- 由于观察者和目标之间不知道对方的细节,观察者只能知道目标出现变化,但不一定知道变化是如何发生的;同时目标通知观察者自己发生变化后,不知道观察者是如何理解和处理变化的。
在IOS 编程中的 观察者模式
KVO 模式。