持续创作,加速成长!这是我参与「掘金日新计划 · 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 <Foundation/Foundation.h>
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<UITouch *> *)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 <Foundation/Foundation.h>
@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 <Foundation/Foundation.h>
#import "Command.h"
NS_ASSUME_NONNULL_BEGIN
@interface CommandManager : NSObject
/// 命令管理容器
@property(nonatomic, copy) NSMutableArray <Command *>*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<UITouch *> *)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 <Foundation/Foundation.h>
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<UITouch *> *)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 <Foundation/Foundation.h>
#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 <Foundation/Foundation.h>
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<UITouch *> *)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 <Foundation/Foundation.h>
//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<UITouch *> *)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: <BusinessA: 0x6000003da650>
// handled: YES
}];
}
发文不易, 喜欢点赞的人更有好运气? :), 定期更新+关注不迷路~
ps:欢迎加入笔者18年建立的研究iOS审核及前沿技术的三千人扣群:662339934,坑位有限,备注“掘金网友”可被群管通过~
© 版权声明
文章版权归作者所有,未经允许请勿转载,侵权请联系 admin@trc20.tw 删除。
THE END