iOS老司机可落地在中大型iOS项目中的5大接地气设计模式合集
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第1天,点击查看活动详情
1. 前言: 设计模式可以在大型项目中有哪些可落地的优化?
- 笔者目前负责一个中大型iOS项目,用PPRows跑一下项目根文件,目前代码量约28W。
- 在这样一个大型项目中,我们组用了哪些方法对业务逻辑及代码架构进行解耦呢?
- 在这种代码量级的项目中,有哪些接地气的,可落地的优化经验呢?
- 在此抛砖引玉,欢迎大家一起相互探讨。
2. 落地
2.1 采用"单例模式", 做一个更加单一职责的广告管理类.
2.1.1 单例模式导图
2.1.2 单例模式, 核心类文件构成
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); } ```
2.2 采用"命令模式", 使行为参数化, 降低代码重合度
2.2.1 命令模式导图
2.2.2 命令模式, 核心类文件构成
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
/// 命令管理者提供单例方法供使用者调用 + (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 ```
2.3 采用"适配器"模式, 更优雅的为陈旧业务代码扩展新功能.
2.3.1 适配器模式导图
2.3.2 适配器模式解耦, 核心类文件构成
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 桥接模式导图
2.4.2 桥接模式解耦, 核心类文件构成
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 责任链模式导图
2.5.2 责任链模式解耦, 核心类文件构成
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:
} ```
发文不易, 喜欢点赞的人更有好运气👍 :), 定期更新+关注不迷路~
ps:欢迎加入笔者18年建立的研究iOS审核及前沿技术的三千人扣群:662339934,坑位有限,备注“掘金网友”可被群管通过~
- iOS老司机聊聊实际项目开发中的<<人月神话>>
- iOS老司机可落地在中大型iOS项目中的5大接地气设计模式合集
- iOS老司机的跨端跨平台Hybrid开发Tips
- iOS老司机的2022年回顾, 聊聊寒冬下的实用<<谈判力>>
- iOS老司机可落地的中大型iOS项目中的设计模式优化Tips_桥接模式
- iOS老司机的多线程PThread学习分享
- iOS老司机整理, iOSer必会的经典算法_2
- iOS老司机的<<蓝海转型>>读书分享
- iOS老司机的<<程序员的自我修养:链接、装载与库>>读书分享
- iOS老司机的接地气算法Tips
- iOS老司机的RunLoop原理探究及实用Tips
- iOS老司机整理, iOSer必会的经典算法_1
- iOS老司机的App启动优化Tips, 让启动速度提升10%
- iOS老司机的网络相关Tips
- 恋上数据结构与算法
- iOS老司机带你一起把App的崩溃率降到0.1%以下
- 探究Swift的String底层实现
- iOS老司机万字整理, 可能是最全的Swift Tips
- iOS老司机可落地的中大型iOS项目中的设计模式优化Tips
- 聊一聊Swift中的闭包