iOS老司机可落地在中大型iOS项目中的5大接地气设计模式合集

语言: CN / TW / HK

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情

1. 前言: 设计模式可以在大型项目中有哪些可落地的优化?

  • 笔者目前负责一个中大型iOS项目,用PPRows跑一下项目根文件,目前代码量约28W。
  • 在这样一个大型项目中,我们组用了哪些方法对业务逻辑及代码架构进行解耦呢?
  • 在这种代码量级的项目中,有哪些接地气的,可落地的优化经验呢?
  • 在此抛砖引玉,欢迎大家一起相互探讨。

image.png

2. 落地

2.1 采用"单例模式", 做一个更加单一职责的广告管理类.

2.1.1 单例模式导图

image.png

2.1.2 单例模式, 核心类文件构成

image.png

2.1.3 采用单例模式解耦, 核心代码文件讲解

2.1.3.1 定义一个继承自NSObject的单例广告管理类ADManager

  • ADManager.h文件 ```

import

NS_ASSUME_NONNULL_BEGIN

@interface ADManager : NSObject

/// 供外部统一使用的单例类方法 + (instancetype)sharedInstance;

@end

NS_ASSUME_NONNULL_END - ADManager.m文件

import "ADManager.h"

@implementation ADManager

  • (instancetype)sharedInstance {     // 静态局部变量     static ADManager *adManager = nil;

// 通过dispatch_once方式, 确保instance在多线程环境下只被创建一次     static dispatch_once_t onceToken;     dispatch_once(&onceToken, ^{         // 调用父类的方法创建实例, 防止跟重写自身的allocWithZone发生循环调用         adManager = [[super allocWithZone:NULL] init];     });     return adManager; }

// 重写自身的allocWithZone, 应对不使用sharedInstance方法直接通过alloc创建对象的情况 + (instancetype)allocWithZone:(struct _NSZone *)zone {     return [self sharedInstance]; }

// MRC下重写copyWithZone, 应对通过copy复制对象的情况, OBJC_ARC_UNAVAILABLE + (id)copyWithZone:(struct _NSZone *)zone {     return self; }

@end ```

2.1.3.2 实际业务使用单例模式示例

``` // //  Viewontroller.m //  appDesignPattern // //  Created by JackLee on 2022/9/21. //

import "ViewController.h"

import "ADManager.h"

@interface ViewController ()

@end

@implementation ViewController

  • (void)viewDidLoad {     [super viewDidLoad]; }

  • (void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event {     // 测试单例模式     [self testSingleADManager]; }

/// 测试单例模式 - (void)testSingleADManager {     ADManager adManager = [ADManager sharedInstance];     ADManager adManager2 = [ADManager new];

NSLog(@"adManager === %@ ptr === %p", adManager, adManager);     NSLog(@"adManager2 === %@ ptr === %p", adManager2, adManager2); } ```

image.png

2.2 采用"命令模式", 使行为参数化, 降低代码重合度

2.2.1 命令模式导图

image.png

2.2.2 命令模式, 核心类文件构成

image.png

2.2.3 命令模式对行为进行参数化处理以解耦, 核心代码文件讲解

2.2.3.1 定义一个抽象命令类Command

  • Command.h文件 ```

import

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

@interface Command : NSObject

// 命令完成的回调 @property (nonatomic, copy) CommandCompletionCallBack completion;

/// 执行命令 - (void)execute; /// 取消命令 - (void)cancel;

/// 完成命令 - (void)done;

@end - Command.m文件

import "Command.h"

import "CommandManager.h"

@implementation Command

  • (void)execute {     // override to subclass, 交给子类复写具体实现 //    [self done]; }

  • (void)cancel {     self.completion = nil; }

  • (void)done {     // 考虑到多线程的情况, 异步回到主队列     dispatch_async(dispatch_get_main_queue(), ^{         if (self.completion) {             self.completion(self);         }                  // 释放         self.completion = nil;         [[CommandManager sharedInstance].arrayCommands removeObject:self];     }); }

@end ```

  • 具体的点赞命令LikedCommand.h ```

import "Command.h"

NS_ASSUME_NONNULL_BEGIN

@interface LikedCommand : Command

@end

NS_ASSUME_NONNULL_END ```

  • 具体的点赞命令LikedCommand.m ```

import "LikedCommand.h"

@implementation LikedCommand

  • (void)execute {     NSLog(@"执行点赞操作 ====");     [self done]; }

@end ```

  • 具体的分享命令ShareCommand.h ```

import "Command.h"

NS_ASSUME_NONNULL_BEGIN

@interface ShareCommand : Command

@end

NS_ASSUME_NONNULL_END ```

  • 具体的分享命令ShareCommand.m ```

import "ShareCommand.h"

@implementation ShareCommand

  • (void)execute {     NSLog(@"执行分享操作 ====");     [self done]; }

@end ```

  • 命令管理类CommandManager.h ```

import

import "Command.h"

NS_ASSUME_NONNULL_BEGIN

@interface CommandManager : NSObject

/// 命令管理容器 @property(nonatomic, copy) NSMutableArray *arrayCommands;

/// 命令管理者提供单例方法供使用者调用 + (instancetype)sharedInstance;

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

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

@end

NS_ASSUME_NONNULL_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; } + (instancetype)allocWithZone:(struct _NSZone *)zone {     return [self sharedInstance]; }

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

  • (void)cancelCommand:(Command *)cmd {     if (cmd) {         // 从命令容器当中移除         [[[self sharedInstance] arrayCommands] 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 ```

2.2.3.2 实际业务使用命令模式示例

``` // //  Viewontroller.m //  appDesignPattern // //  Created by JackLee on 2022/9/21. //

import "ViewController.h"

import "LikedCommand.h"

import "ShareCommand.h"

import "CommandManager.h"

@interface ViewController ()

@end

@implementation ViewController

  • (void)viewDidLoad {     [super viewDidLoad]; }

  • (void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event {     // 测试命令模式     [self testCommand]; }

/// 测试命令模式 - (void)testCommand {     LikedCommand liked = [LikedCommand new];     ShareCommand share = [ShareCommand new];

[CommandManager executeCommand:liked completion:^(Command *cmd) {         NSLog(@"点赞命令完成回调 cmd === %@", cmd);     }];

[CommandManager executeCommand:share completion:^(Command *cmd) {         NSLog(@"分享命令完成回调 cmd === %@", cmd);     }]; }

@end ```

image.png

2.3 采用"适配器"模式, 更优雅的为陈旧业务代码扩展新功能.

2.3.1 适配器模式导图

image.png

2.3.2 适配器模式解耦, 核心类文件构成

image.png

2.3.3 适配器模式解耦, 核心代码文件讲解

2.3.3.1 定义一个继承自NSObject的适配器类AdapterTarget

  • AdapterTarget.h文件 ```

import "OldTarget.h"

NS_ASSUME_NONNULL_BEGIN

/// 适配对象 @interface AdapterTarget : NSObject

/// 被适配老业务对象 @property (nonatomic, strong) OldTarget *oldTarget;

/// 对原有方法进行适配器包装 - (void)adapertRequest;

@end

NS_ASSUME_NONNULL_END - AdapterTarget.m文件 // //  AdapterTarget.m //  appDesignPattern // //  Created by JackLee on 2022/10/12. //

import "AdapterTarget.h"

@implementation AdapterTarget

  • (OldTarget *)oldTarget {     if (!_oldTarget) {         _oldTarget = [[OldTarget alloc] init];     }     return _oldTarget; }

  • (void)adapertRequest {     NSLog(@"新增额外处理A");

[self.oldTarget oldOperation];

NSLog(@"新增额外处理B"); }

@end ```

  • 久经考验的处理业务逻辑的旧类 OldTarget
  • OldTarget.h文件 ```

import

NS_ASSUME_NONNULL_BEGIN

@interface OldTarget : NSObject

/// 久经考验的旧处理方法 - (void)oldOperation;

@end

NS_ASSUME_NONNULL_END ```

  • OldTarget.m文件 ```

import "OldTarget.h"

@implementation OldTarget

  • (void)oldOperation {     NSLog(@"久经考验的旧处理方法"); }

@end ```

2.3.3.2 实际业务使用适配器模式示例

``` // //  ViewController.m //  appDesignPattern // //  Created by JackLee on 2022/9/21. //

import "ViewController.h"

import "AdapterTarget.h"

@interface ViewController ()

@end

@implementation ViewController

  • (void)viewDidLoad {     [super viewDidLoad]; }

  • (void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event {     // 测试适配器模式     [self testAdapter]; }

  • (void)testAdapter {     AdapterTarget *adapterTarget = [AdapterTarget new];     [adapterTarget adapertRequest]; } ```

2.4 采用"桥接模式", 应对同一页面网络数据接口来回变动的场景, 进行逻辑解耦.

2.4.1 桥接模式导图

image.png

2.4.2 桥接模式解耦, 核心类文件构成

image.png

2.4.3 桥接模式解耦, 核心代码文件讲解

2.4.3.1 定义一个抽象的基类BaseObjectA

  • BaseObjectA.h文件 ```

import

import "BaseObjectB.h"

NS_ASSUME_NONNULL_BEGIN

@interface BaseObjectA : NSObject

/// 桥接模式的核心实现 @property (nonatomic, strong) BaseObjectB *objB;

/// 获取数据 - (void)handle;

@end

NS_ASSUME_NONNULL_END - BaseObjectA.m文件

import "BaseObjectA.h"

@implementation BaseObjectA

/*  A1 --> B1、B2 2种对应  A2 --> B1、B2 2种对应  / - (void)handle {     // override to subclass 交给具体的子类复写     [self.objB fetchData]; }

@end ```

  • 具体的页面A1 ObjectA1
  • ObjectA1.m文件 ```

import "ObjectA1.h"

@implementation ObjectA1

  • (void)handle {     // before 业务逻辑操作

[super handle];

// after 业务逻辑操作 }

@end ```

  • 不同接口的抽象父类BusinessB.h文件 ```

import

NS_ASSUME_NONNULL_BEGIN

@interface BaseObjectB : NSObject

  • (void)fetchData;

@end

NS_ASSUME_NONNULL_END ```

  • 不同接口的抽象父类BusinessB.m文件 ```

import "BaseObjectB.h"

@implementation BaseObjectB

  • (void)fetchData {     // override to subclass 交给子类实现 }

@end ```

  • 具体使用的接口B2的实现ObjectB.m文件 ```

import "ObjectB2.h"

@implementation ObjectB2

  • (void)fetchData {     // B2具体的逻辑处理     NSLog(@"B2接口获取数据具体的逻辑处理 === "); }

@end ```

2.4.3.2 实际业务使用桥接模式示例

``` // //  ViewController.m //  appDesignPattern // //  Created by JackLee on 2022/9/21. //

import "ViewController.h"

import "ObjectA1.h"

import "ObjectA2.h"

import "ObjectB1.h"

import "ObjectB2.h"

@interface ViewController ()

@end

@implementation ViewController

  • (void)viewDidLoad {     [super viewDidLoad]; }

  • (void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event {     // 测试桥接模式     [self testBridgeFetchData]; }

  • (void)testBridgeFetchData {     /*      根据实际业务判断使用哪套具体数据      A1 --> B1、B2 2种对应      A2 --> B1、B2 2种对应      /

// 创建一个具体的ClassA     BaseObjectA objA = [ObjectA1 new]; //    BaseObjectA objA = [ObjectA2 new];

// 创建一个具体的ClassB //    BaseObjectB objB2 = [ObjectB1 new];     BaseObjectB objB2 = [ObjectB2 new];     // 将一个具体的ClassB2 指定给抽象BaseClassB     objA.objB = objB2;

// A列表使用B2的接口获取数据     [objA handle]; } ```

2.5 采用"责任链模式", 应对产品大大提出的业务变更, 对业务代码进行解耦.

2.5.1 责任链模式导图

image.png

2.5.2 责任链模式解耦, 核心类文件构成

image.png

2.5.3 责任链模式解耦, 核心代码文件讲解

2.5.3.1 定义一个抽象的基类BusinessObject

  • BusinessObject.h文件 ```

import

//NS_ASSUME_NONNULL_BEGIN

@class BusinessObject;

/// 某一业务完成之后, 返回的结果是否有处理掉这个业务 typedef void(^CompletionBlock)(BOOL handled); /// 这个业务对应的处理者, 有没有处理好这个业务 typedef void(^ResultBlock)(BusinessObject *handler, BOOL handled);

@interface BusinessObject : NSObject

/// 下一个响应者(响应链构成的关键) @property (nonatomic, strong) BusinessObject *nextBusiness;

/// 响应者的处理方法 - (void)handle:(ResultBlock)result; /// 各个业务在该方法当中做实际业务处理, 完成之后结果返回给调用方 - (void)handleBusiness:(CompletionBlock)completion;

@end - BusinessObject.m文件

import "BusinessObject.h"

@implementation BusinessObject

/// 责任链入口方法 -(void)handle:(ResultBlock)result {     CompletionBlock completion = ^(BOOL handled){         // 当前业务处理掉了,上抛结果         if (handled) {             result(self, handled);         }         else {             // 沿着责任链,指派给下一个业务处理             if (self.nextBusiness) {                 [self.nextBusiness handle:result];             }             else {                 // 没有业务处理,上抛                 result(nil, NO);             }         }     };    

// 当前业务进行处理     [self handleBusiness:completion]; }

  • (void)handleBusiness:(CompletionBlock)completion {

/*      业务逻辑处理      例如异步网络请求、异步本地照片查询等      交给子类复写      /

}

@end ```

  • 例如需要处理网络请求的业务A BusinessA
  • 业务A的BusinessObject.h文件 ```

import "BusinessObject.h"

NS_ASSUME_NONNULL_BEGIN

@interface BusinessA : BusinessObject

@end

NS_ASSUME_NONNULL_END ```

  • 业务A的BusinessObject.m文件 ```

import "BusinessA.h"

@implementation BusinessA

  • (void)handleBusiness:(CompletionBlock)completion {     NSLog(@"处理业务A");

// 业务顺序: A -> B -> C //    completion(NO);

// 业务顺序: C -> B -> A     completion(YES); }

@end ```

  • 业务B的BusinessObjectB.h文件 ```

import "BusinessObject.h"

NS_ASSUME_NONNULL_BEGIN

@interface BusinessB : BusinessObject

@end

NS_ASSUME_NONNULL_END ```

  • 业务B的BusinessObjectB.m文件 ```

import "BusinessB.h"

@implementation BusinessB

  • (void)handleBusiness:(CompletionBlock)completion {

NSLog(@"处理业务B");

// 业务顺序: A -> B -> C //    completion(NO);

// 业务顺序: C -> B -> A     completion(NO); }

@end ```

  • 业务C的BusinessObjectC.h文件 ```

import "BusinessObject.h"

NS_ASSUME_NONNULL_BEGIN

@interface BusinessC : BusinessObject

@end

NS_ASSUME_NONNULL_END ```

  • 业务C的BusinessObjectC.m文件 ```

import "BusinessC.h"

@implementation BusinessC

  • (void)handleBusiness:(CompletionBlock)completion {

NSLog(@"处理业务C");

// 业务顺序: A -> B -> C //    completion(YES);

// 业务顺序: C -> B -> A     completion(NO); }

@end ```

2.5.3.2 实际业务使用责任链模式方法

``` // //  ViewController.m //  appDesignPattern // //  Created by JackLee on 2022/9/21. //

import "ViewController.h"

import "BusinessA.h"

import "BusinessB.h"

import "BusinessC.h"

import "appDesignPattern-Swift.h"

@interface ViewController ()

@end

@implementation ViewController

  • (void)viewDidLoad {     [super viewDidLoad]; }

  • (void)touchesBegan:(NSSet )touches withEvent:(UIEvent )event {     [self testBusiness]; }

  • (void)testBusiness {

BusinessA businessObjA = [BusinessA new];     BusinessB businessObjB = [BusinessB new];     BusinessC *businessObjC = [BusinessC new];     // 业务顺序: A -> B -> C //    businessObjA.nextBusiness = businessObjB; //    businessObjB.nextBusiness = businessObjC;

// 业务顺序: C -> B -> A     businessObjC.nextBusiness = businessObjB;     businessObjB.nextBusiness = businessObjA;

// 响应者的处理方法, 责任链入口方法     /      1. 当前业务处理掉了, 上抛结果      2. 当前业务没处理掉, 沿着责任链, 指派给下一个业务处理, 如果没有业务处理, 继续上抛      3. 对当前业务进行处理      handler:      handled: 业务处理结果      /

// 业务顺序: A -> B -> C //    [businessObjA handle:^(BusinessObject *handler, BOOL handled) { // // //    }];

// 业务顺序: C -> B -> A     [businessObjC handle:^(BusinessObject *handler, BOOL handled) {         // handler:         // handled: YES     }];    

} ```

发文不易, 喜欢点赞的人更有好运气👍 :), 定期更新+关注不迷路~

ps:欢迎加入笔者18年建立的研究iOS审核及前沿技术的三千人扣群:662339934,坑位有限,备注“掘金网友”可被群管通过~