[iOS面试]第9章 设计模式相关面试问题
注意:本文主讲设计模式相关面试问题,包括 常用六大设计原则、责任链、桥接、适配器、单例、命令。
一、六大设计原则
- 单一职责原则
- 依赖倒置原则
- 开闭原则
- 里氏替换原则
- 接口隔离原则
- 迪米特法则
1)单一职责原则
- 一个类只做一件事
比如: UIView 和 CALayer关系, UIView只负责事件传递和事件响应,CALayer专门负责动画以及视图的展示
2)依赖倒置原则
- 抽象接口不依赖于具体实现, 具体实现可以依赖于抽象接口
比如定义一些处理数据访问,增删改查接口方法时,所有上层的业务调用都应该依赖于定义的抽象接口. 至于接口内部具体实现是用数据库,还是用文件,以及用plist, userdefault, 对上层业务来说是感知不到的, 这就体现了通过抽象接口反转依赖.对于上层业务只依赖于接口定义,比如增删改查,对于内部采用哪种存储方案,上层业务是不关注的.也没必要把具体存储方案暴露给使用方.
3)开闭原则
- 对修改关闭, 对扩展开放
比如:定义一个类考虑后期扩展性和灵活性. 前期针对一个需求,要考虑多个版本的迭代需求. 对类的成员变量定义尽量谨慎,避免后面频繁修改这个类.类的数据结构定义后,后面就是根据需求提供接口问题, 包括子类继承.
4)里氏替换原则
- 父类可以被子类无缝替换, 且原有功能不受任何影响
比如: KVO机制
5)接口隔离原则
- 使用多个专门的协议,而不是一个庞大臃肿的协议
- 协议中方法应当尽量少
比如: UITableViewDelegate专门处理TableView回调代理事件
UITableViewDateSource专门用于获取TableView数据源
6)迪米特法则
- 一个对象对其他对象有尽可能少的了解
- 高内聚,低耦合
因为模块和模块之间, 要想解除耦合,体现出编码规范让对象尽量少的知道其他对象都有什么,包括成员变量,属性以及它的方法.
二、责任链
责任链模式可以解决任务的依赖关系问题.
举例:
一个对象中有一个属性叫nextResponder, 该属性与当前对象属于同一个类, 当当前对象不能执行某个方法时, 就调用他的nextResponder来执行同样的方法, 将任务传递下去.
使用这种方式时, 当任务的依赖顺序发生改变时, 只需要调整其nextResponder的指向即可调整他的响应链顺序.
我们还可以根据后端给出的响应来动态调整响应链顺序, 系统中使用责任链的设计模式处理了UI事件的传递机制
三、桥接
使用者持有抽象的基类ClassA, ClassA持有抽象的基类B, 调用时, 根据需要将A1/A2/A3赋值给调用者原先持有的属性, 在将B1/B2/B3根据需求赋值给ClassA, 这样我们可以自由组合调用者和其内部的实际调用, 而作为桥梁的ClassA与ClassB本身不关心他们的实际调用.
问题:怎样理解桥接设计模式?(简单语言描述桥接模式是什么?)
桥接模式可以解决 列表和多套数据耦合的问题.
四、适配器
一个现有类需要适应变化的问题
适配器分为: 对象适配器
类适配器.
当我们有个一个年代久远的基类, 我们不便对其添加对象和方法时, 我们就可以为其添加适配器
, 使一个类持有该基类, 在调用这个基类的前后我们可以插入自己的代码, 来适配新的需求. 这也就是做了一次简单的封装.
- (void)request{
//适配逻辑
[被适配对象 某方法]
//适配逻辑
}
五、单例模式
- 在单例方法中创建实例使用
[[super allocWithZone:Null] init];
- 重写自身的allocWithZone方法在其内部调用单例方法保证创建永远是一个单例
- 遵守Copy协议, 重写copyWithZone方法, 返回self, 令其不能被copy
六、命令模式
问题:命令模式主要表达的是什么?我们一般用命令模式干什么?
- 命令模式用来做
行为参数化
- 命令模式用来
减低代码重合度
我们可以将一些事件拆分开来, 事件的执行及其回调上下文等封装成commend对象, 调用commend的管理者封装为Manager单例对象, 以此来减少代码的分散, 调用时被转换成, 生成命令, 中央管理器执行命令的命令模式. 这和NSUrlSession使用的task模式类似, 创建task, 执行task的感觉
设计模式面试总结:
你都知道哪些设计原则, 请谈谈你的理解?
答: 单一职责原则, 依赖倒置原则, 开闭原则, 里氏替换原则, 接口隔离原则, 迪米特法则.能否用一幅图简单的表示桥接模式的主体结构?
UI事件的传递机制是怎样实现的?你对其中运用到的设计模式是怎样理解的?
答:
UI视图角度回答
设计模式角度回答 -> 责任链模式.手写单例实现
重写allcoWithZone/copyWithZone方法, 使用[super allocWithZone:]创建单例
+ (id)sharedInstance {
// 静态局部变量
static Mooc *instance = nil;
// 通过dispatch_once方式 确保instance在多线程环境下只被创建一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 创建实例
instance = [[super allocWithZone:NULL] init];
});
return instance;
}
// 重写方法【必不可少】
+ (id)allocWithZone:(struct _NSZone *)zone{
return [self sharedInstance];
}
// 重写方法【必不可少】
- (id)copyWithZone:(nullable NSZone *)zone{
return self;
}