全方位剖析iOS高级技术问题(九)之设计模式

838 阅读6分钟

本文主要内容

一.六大设计原则
二.责任链模式
三.桥接模式
四.适配器模式
五.单例模式
六.命令模式

截屏2022-09-20 15.21.34.png

一.六大设计原则

  • 单一职责原则:一个类只负责一件事,例如UIView和CALayer

  • 开闭原则:对修改关闭、对扩展开放,例如对类的定义

  • 接口隔离原则:使用多个专门的协议,而不是一个庞大臃肿的协议,例如UITableView的Delegate和DataSource;

  • 依赖倒置原则:抽象不应该依赖于具体实现,具体实现可以依赖于抽象,例如定义数据访问,增删改查的接口

  • 里氏替换原则:父类可以被子类无缝替换,且原有功能不受任何影响,例如KVO机制,既使用观察者模式,又遵循里氏替换原则

  • 迪米特法则:一个对象应当对其他对象有尽可能少的了解,高内聚、低耦合,例如对象之间尽可能独立

截屏2022-09-20 15.24.15.png

二.责任链模式

应用程序框架包括一个称为响应者链的架构。该链由一系列响应者事件或操作信息被传递并最终被处理。如果给定的响应者对象不处理特定消息,它会将消息传递给链中的下一个响应者。

引出问题:一个关于需求变更的问题

截屏2022-09-20 15.58.40.png

截屏2022-09-22 09.51.10.png

  • 类构成

截屏2022-09-20 15.59.44.png

2.1、责任链使用场景

  • 有多个对象可以处理请求,而处理程序只有在运行时才能确定;
  • 向一组对象发出请求,而不想显式指定请求的特定处理程序。

2.2 责任链模式的优缺点

优点

  • 降低耦合度。它将请求的发送者和接受者解耦;
  • 简化了对象。使得对象不需要知道链的结构;
  • 增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责;
  • 增加新的请求处理类很方便。

缺点

  • 不能保证请求一定被接收;
  • 系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用;
  • 可能不容易观察运行时的特征,有碍于除错。

三.桥接模式

引出问题:一个关于业务解耦的问题

截屏2022-09-22 10.14.46.png

  • 类构成

截屏2022-09-22 10.16.32.png

四.适配器模式

一个现有类需要适应变化的问题。

  • 对象适配器(常见)
    类构成

截屏2022-09-22 10.26.17.png

- (void)request {
    // 适配逻辑
    
    [被适配对象 某方法];
    
    // 适配逻辑
}

对象适配器实现

// 原有类
#import "Target.h"

@implementation Target
- (void)operation {
    // 原有的具体业务逻辑
}
@end
// 适配对象类
#import "Target.h"
// 适配对象
@interface CoolTarget: NSObject

// 被适配对象
@property (nonatomic, strong) Target *target;

//对原有方法包装
- (void)request;
@end

#import "CoolTarget.h"
@implementation CoolTarget

- (void)request {
    // 额外处理
    
    [self.target operation];
    
    // 额外处理
}
@end

  • 类适配器

五.单例模式

  • 单例模式的实现
+ (id)sharedInstance {
    // 静态局部变量
    static Juejin *instance = nil;
    
    // 通过dispatch_once方式,确保instance在多线程环境下只被创建一次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 创建实例(规避循环调用)
        instance = [[super allocWithZone: NULL] init];
    });
    return instance;
}

// 重写方法【必不可少】:避免不使用sharedInstance创建实例,直接用allocWithZone创建
+ (id)allocWithZone:(struct _NSZone *)zone {
    return [self sharedInstance];
}

// 重写方法【必不可少】:避免外界使用copyWithZone创建实例
- (id)copyWithZone:(nullable NSZone *)zone {
    return self;
}

六.命令模式

  • 行为参数化;
  • 降低代码重合度。
// Command.h
#import <Foundation/Foundation.h>

@class Command;
typedef void(^CommandCompletionCallBack)(Command* cmd)

@interface Command: NSObject
@property (nonatomic, copy) CommandCompletitonCallBack completion;
// 执行
- (void)execute;
// 取消
- (void)cancel;
// 完成
- (void)done;
@end

// Command.m
#import "CommandManager.h"
@implementation Command

- (void)execute {
    // override to subclass
    [self done];
}

- (void)cancel {
    // 回调置为nil 
    self.completion = nil;
}

- (void)done {
    // 异步回到主队列 
    dispatch_async(dispatch_get_main_queue(), ^{
        if (_completion) {
            _completion(self);
        }
        
        // 释放,防止循环
        self.completion = nil;
        
        [[CommandManager sharedInstance].arrayCommands removeObject: self];
    });
}

命令管理者

// CommandManager.h
#import <Foundation/Foundation.h>
#import "Command.h"
@interface CommandManager : NSObject
// 命令管理容器
@porperty (nonatomic, strong) NSMutableArray <Command*> *arrayCommands;

// 命令管理者以单例方式呈现
+ (instancetype)sharedInstance;

// 执行命令
+ (void)executeCommand:(Command *)cmd completion:(CommandCompletionCallBack)completion;

// 取消命令
+ (void)cancelCommand:(Command *)cmd;
@end

// CommandManager.m
#import "CommandManager.h"
@implementation CommandManager

// 命令管理者以单例方式呈现
+ (instancetype)sharedInstance {
    static CommandManager *instance = nil
    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;
}

// 初始化方法
- (id)init {
    self = [super init];
    if(self) {
        // 初始化命令容器
        _arrayCommands = [NSMutableArray array];
    }
    return self;
}

// 执行命令
+ (void)executeCommand:(Command *)cmd completion:(CommandCompletionCallBack)completion {
    if (cmd) {
        // 如果命令正在执行不做处理,否则添加并执行命令
        if (![self _isExecutingCommand: cmd]) {
            // 添加到到命令容器中
            [[[self sharedInstance] arraryCommands] addObject: cmd];
            // 设置命令执行完成的回调
            cmd.completion = completion;
            // 执行命令
            [cmd execute];
        }
    }
}

// 取消命令
+ (void)cancelCommand:(Command *)cmd {
    [[[self sharedInstance] arraryCommands] removeObject: cmd];
    
    // 取消执行命令 
    [cmd cancel];
}

// 判断当前命令是否正在执行
+ (BOOL)_isExecutingCommand:(Command *)cmd {
    if (cmd) {
        NSArray *cmds = [[self sharedInstance] arrayCommands];
        for (command *aCmd in cmds) {
            // 当前命令正在执行
            if (cmd == aCmd) {
                return YES;
            }
        }
    }
    return NO;
}
@end

本文总结

问题1:手写单例实现

+ (id)sharedInstance {
    // 静态局部变量
    static Juejin *instance = nil;
    
    // 通过dispatch_once方式,确保instance在多线程环境下只被创建一次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 创建实例(规避循环调用)
        instance = [[super allocWithZone: NULL] init];
    });
    return instance;
}

// 重写方法【必不可少】:避免不使用sharedInstance创建实例,直接用allocWithZone创建
+ (id)allocWithZone:(struct _NSZone *)zone {
    return [self sharedInstance];
}

// 重写方法【必不可少】:避免外界使用copyWithZone创建实例
- (id)copyWithZone:(nullable NSZone *)zone {
    return self;
}

问题2:了解哪些设计原则,谈谈你的理解

六大设计原则:

  • 1.单一职责原则:一个类只负责一件事,例如UIView和CALayer

  • 2.开闭原则:对修改关闭、对扩展开放,例如对类的定义

  • 3.接口隔离原则:使用多个专门的协议,而不是一个庞大臃肿的协议,例如UITableView的Delegate和DataSource

  • 4.依赖倒置原则:抽象不应该依赖于具体实现,具体实现可以依赖于抽象,例如定义数据访问,增删改查的接口

  • 5.里氏替换原则:父类可以被子类无缝替换,且原有功能不受任何影响,例如KVO机制,既使用观察者模式,又遵循里氏替换原则

  • 6.迪米特法则:一个对象应当对其他对象有尽可能少的了解,高内聚、低耦合,例如对象之间尽可能独立

问题3:图示简单表示桥接模式的主题结构

定义一个抽象父类A和抽象父类B,把抽象父类B作为抽象父类A的一个成员值,如此就可以衍生出从A1到B1、从A1到B2、从A1到B3的联系。 截屏2022-09-22 10.16.32.png

问题4:UI事件传递机制是怎样实现的?对其中运用到的设计模式是怎样理解?

责任链模式。一个类有一个成员变量(也是当前类的类型)。

有任何问题,欢迎👏各位评论指出!觉得博主写的还不错的麻烦点个赞喽👍