面试题答案
一键面试利用单例模式优化观察者模式
- 设计思路:
- 将观察者的注册中心设计为单例。在大型项目中,所有的观察者注册和通知逻辑都通过这个单例对象来管理。这样可以避免多个注册中心实例带来的资源浪费和不一致问题。
- 单例对象维护一个字典(或其他合适的数据结构)来存储被观察对象及其对应的观察者列表。当被观察对象状态改变时,通过单例注册中心找到对应的观察者列表并进行通知。
- 可能遇到的问题:
- 线程安全问题:在多线程环境下,单例对象的访问以及注册中心数据结构的读写操作可能导致数据不一致。例如,一个线程正在注册观察者,另一个线程同时在通知观察者,可能会导致数据冲突。
- 内存管理问题:如果观察者和被观察对象之间存在循环引用,可能会导致内存泄漏。比如,被观察对象强引用观察者列表,而观察者又强引用被观察对象。
- 解决方案:
- 线程安全:使用
dispatch_once
来创建单例,保证单例在多线程环境下只被创建一次。对于注册中心数据结构的读写操作,使用dispatch_queue
来保证线程安全。例如,在注册和移除观察者时,将操作放在一个串行队列中执行。 - 内存管理:使用弱引用来打破循环引用。在观察者列表中对观察者使用弱引用,这样当观察者不再被其他对象强引用时,其内存可以被正常释放。
- 线程安全:使用
利用工厂模式优化观察者模式
- 设计思路:
- 利用工厂模式创建观察者和被观察对象。这样可以将对象的创建逻辑封装在工厂类中,便于集中管理和维护。例如,当业务需求变化,需要创建不同类型的观察者或被观察对象时,只需要在工厂类中修改创建逻辑,而不需要在整个项目中查找和修改创建对象的代码。
- 工厂可以根据配置或运行时条件创建不同具体类型的观察者和被观察对象。例如,根据用户权限创建具有不同功能的观察者。对于被观察对象,工厂可以创建不同性能优化版本的实例,如轻量级版本用于资源受限场景。
- 可能遇到的问题:
- 工厂类的复杂性:随着项目的发展,工厂类可能变得非常庞大,包含各种复杂的创建逻辑。这会导致工厂类难以维护和理解。
- 增加耦合度:如果项目中过多依赖工厂模式来创建对象,可能会增加模块之间的耦合度。例如,某个模块依赖工厂类创建特定对象,当工厂类的接口或创建逻辑改变时,依赖它的模块也需要相应修改。
- 解决方案:
- 工厂类的复杂性:对工厂类进行职责划分,采用多个小的工厂类或者使用工厂方法模式。每个小工厂类负责创建一类相关的对象,这样可以降低单个工厂类的复杂度。
- 增加耦合度:通过依赖注入的方式,将工厂创建的对象注入到需要使用的模块中,而不是让模块直接依赖工厂类。这样当工厂类的实现改变时,只要注入的对象接口不变,依赖它的模块就不需要修改。同时,可以使用抽象工厂模式,通过抽象接口来提供对象创建服务,进一步降低耦合度。