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

持续创作,加速成长!这是我参与「掘金日新计划 · 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 <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);
}

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 <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

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 <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 桥接模式导图

image.png

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

image.png

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 责任链模式导图

image.png

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

image.png

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,坑位有限,备注“掘金网友”可被群管通过~

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYUHvjVs' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片