[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{
  //适配逻辑
  [被适配对象 某方法]
 //适配逻辑
}

五、单例模式

  1. 在单例方法中创建实例使用
[[super allocWithZone:Null] init];
  1. 重写自身的allocWithZone方法在其内部调用单例方法保证创建永远是一个单例
  2. 遵守Copy协议, 重写copyWithZone方法, 返回self, 令其不能被copy

六、命令模式

问题:命令模式主要表达的是什么?我们一般用命令模式干什么?

  • 命令模式用来做 行为参数化
  • 命令模式用来 减低代码重合度

我们可以将一些事件拆分开来, 事件的执行及其回调上下文等封装成commend对象, 调用commend的管理者封装为Manager单例对象, 以此来减少代码的分散, 调用时被转换成, 生成命令, 中央管理器执行命令的命令模式. 这和NSUrlSession使用的task模式类似, 创建task, 执行task的感觉

设计模式面试总结:

  1. 你都知道哪些设计原则, 请谈谈你的理解?
    答: 单一职责原则, 依赖倒置原则, 开闭原则, 里氏替换原则, 接口隔离原则, 迪米特法则.

  2. 能否用一幅图简单的表示桥接模式的主体结构?

  3. UI事件的传递机制是怎样实现的?你对其中运用到的设计模式是怎样理解的?
    答:
    UI视图角度回答
    设计模式角度回答 -> 责任链模式.

  4. 手写单例实现
    重写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;
}