iOS 开发中的模式

在了解设计模式之前先了解下设计时的原则;

1. 设计原则

1.1 单一职责原则; 一个类只负责一个功能;
UIViewCALayer的关系;这篇文章结尾
1.2 开闭原则; 对修改关闭, 对扩展开放;
后期迭代类, 函数, 功能模块时尽量不去更改, 而是通过继承和合成复用方式解决问题;
1.3 接口隔离原则; 将协议细分为多个专门的协议, 而不是一个庞大的多功能协议;
UITableViewdataSourcedelegate的模式, 一个负责数据一个负责处理代理回调;
1.4 依赖倒置原则; 抽象内容不应该依赖具体实现, 具体实现可以依赖于抽象;
1.5 里氏替换原则; 父类可以被子类无缝替换, 且原有功能不受影响;
KVO的实现过程, 中系统自动创建NSKVONotifying_子类替换原有类进而实现功能;
1.6 迪米特法则; 一个对象应该尽量少的去处理管理其他对象;
实现高内聚低耦合;模块之间的解耦;
1.7 合成复用原则; 尽量使用对象组合来达到复用的目的, 而不是继承;
继承时一旦基类发生变化, 那么他的派生类都会跟着变化;RXSwift中有才采用这种策略;


2. 设计模式

2.1 责任链模式;

责任链模式当主要思想是对象引用同一个类型的另一个对象; 每个对象的实现方法都一样, 这样可以形成一种模式就是如果当前对象不处理这个任务, 就把他抛给链上的另一个对象;实现方式有继承自我实现(next指针)?等方式;
Cocoa中的经典用法就是UI的响应链;
优点:

  • 1.解耦请求的发送者和处理者;
  • 2.简化对象, 不需要关心结构;
  • 3.链内的成员可以随时增减;

缺点:

  • 1.出现问题调试较为麻烦;
  • 2.任务并不一定保证能能被处理掉;

责任链实现的代码讲解:

#链条开始的类

#import <Foundation/Foundation.h>
@class HandleModel;
typedef void(^HandleSuccess)(BOOL handled);
typedef void(^HanleResult)(HandleModel * _Nullable handler, BOOL handled);

NS_ASSUME_NONNULL_BEGIN

@interface HandleModel : NSObject
///下一个响应者, 构成响应链的关键
@property (nonatomic, strong) HandleModel   *nextHandler;
//响应者的处理方法
- (void)handle:(HanleResult)resultB taskType:(NSInteger)type;

///各个业务在该方法中做实际的处理事宜
- (void)handleBusiess:(HandleSuccess)callB taskType:(NSInteger)type;
@end
NS_ASSUME_NONNULL_END

#import "HandleModel.h"
@implementation HandleModel
///责任链入口方法, 如果当前类不处理就向链条的下一个指派
- (void)handle:(HanleResult)resultB taskType:(NSInteger)type{
    HandleSuccess successB = ^(BOOL success){
        if (success) {
            resultB(self, success);
        }else {
            ///当前不处理, 沿着责任链, 指派给下一个业务处理
            if (self.nextHandler) {
                [self.nextHandler handle:resultB taskType:type];
            }else {
                ///没有处理者, 传为nil
                resultB(nil, NO);
            }
        }
    };
    ///当前业务进行处理
    [self handleBusiess:successB taskType:type];
}
- (void)handleBusiess:(HandleSuccess)callB taskType:(NSInteger)type{
    if (type < 10) {
        ///执行逻辑处理, 处理完成后回调
        callB(YES);
     }else {
        callB(NO);
     }
}
@end
#链条上的另一个响应者实现, 继承自链条开始的类

#import "HandleModel.h"

NS_ASSUME_NONNULL_BEGIN
@interface HandleModelA : HandleModel

@end
NS_ASSUME_NONNULL_END
#import "HandleModelA.h"
@implementation HandleModelA

//这个方法继承自父类, 逻辑不需变动, 如果当前类不处理就向链条的下一个指派
//- (void)handle:(HanleResult)resultB taskType:(NSInteger)type {
//    HandleSuccess successB = ^(BOOL success){
//        if (success) {
//            resultB(self, YES);
//        }else {
//            if (self.nextHandler) {
//                [self.nextHandler handle:resultB taskType:type];
//            }else {
//                resultB(nil, NO);
//            }
//        }
//    };
//    [self handleBusiess:successB taskType:type];
//}

- (void)handleBusiess:(HandleSuccess)callB taskType:(NSInteger)type{
    if (type >= 10 && type < 20) {
        ///执行逻辑处理, 处理完成后回调
        callB(YES);
     }else {
        callB(NO);
     }
}

@end

实际运用:

- (void)responsiblityChain {
    ///这个数组作用是: 模拟一系列需要处理的任务
    NSArray *taskArr = @[@(1), @(3), @(11), @(15), @(45), @(23), @(80), @(24), @(66)];
    HandleModel *taskModel = [[HandleModel alloc] init];
    HandleModel *taskModelA = [[HandleModelA alloc] init];
    HandleModel *taskModelB = [[HandleModelB alloc] init];
    HandleModel *taskModelC = [[HandleModelC alloc] init];
    ///创建一个责任链, 如果当前模型不处理, 就把任务向链中的下一个模型抛
    taskModel.nextHandler = taskModelA;
    taskModelA.nextHandler = taskModelB;
    taskModelB.nextHandler = taskModelC;

    ///模拟执行多个任务
    for (NSNumber *taskNum in taskArr) {
        [taskModel handle:^(HandleModel *handler, BOOL handled) {
            NSLog(@"任务编号: %@ 执行状态: %d   执行者: %@", taskNum, handled, handler.class);
        } taskType:taskNum.integerValue];
    }
}

#执行结果为
2019-05-05 16:10:26.395055+0800 DesignMode[7894:497216] 任务编号: 1 执行状态: 1   执行者: HandleModel
2019-05-05 16:10:26.395261+0800 DesignMode[7894:497216] 任务编号: 3 执行状态: 1   执行者: HandleModel
2019-05-05 16:10:26.395370+0800 DesignMode[7894:497216] 任务编号: 11 执行状态: 1   执行者: HandleModelA
2019-05-05 16:10:26.395509+0800 DesignMode[7894:497216] 任务编号: 15 执行状态: 1   执行者: HandleModelA
2019-05-05 16:10:26.395654+0800 DesignMode[7894:497216] 任务编号: 45 执行状态: 1   执行者: HandleModelC
2019-05-05 16:10:26.395768+0800 DesignMode[7894:497216] 任务编号: 23 执行状态: 1   执行者: HandleModelB
2019-05-05 16:10:26.395895+0800 DesignMode[7894:497216] 任务编号: 80 执行状态: 0   执行者: (null)
2019-05-05 16:10:26.396014+0800 DesignMode[7894:497216] 任务编号: 24 执行状态: 1   执行者: HandleModelB
2019-05-05 16:10:26.396124+0800 DesignMode[7894:497216] 任务编号: 66 执行状态: 0   执行者: (null)

责任链-示例代码

2.2 桥接模式;

应用场景, 一个VC要适配多套数据模型时; 使用桥接模式能优化处理;

桥接模式的结构示例

代码讲解示例

#抽象业务类持有属性抽象数据请求类

#import <Foundation/Foundation.h>
#import "RequestModel.h"

NS_ASSUME_NONNULL_BEGIN
@interface BusiessModel : NSObject
///桥接模式的核心实现, 抽象类持有;
@property (nonatomic, strong) RequestModel *requestModel;

///处理业务
- (void)handleTask;

@end
NS_ASSUME_NONNULL_END


#.m实现
#import "BusiessModel.h"

@implementation BusiessModel

/**
 具体实现的时候会有四种组合去处理业务
 BusinessA ---> Request1   Request2
 BusinessB ---> Request1   Request2
 */
- (void)handleTask {
    [self.requestModel requestData];
}

@end
#请求类的实例

#import "RequestModel.h"

NS_ASSUME_NONNULL_BEGIN
@interface Request1 : RequestModel

///实例实现
- (void)requestData;

@end
NS_ASSUME_NONNULL_END


#.m实现
#import "Request1.h"

@implementation Request1

- (void)requestData {
    NSLog(@"获取数据2");
}


@end
#业务类的实例过程
#import "BusiessModel.h"

NS_ASSUME_NONNULL_BEGIN
@interface BusiessA : BusiessModel

///实例实现
- (void)handleTask;

@end
NS_ASSUME_NONNULL_END


#.m实现
#import "BusiessA.h"
@implementation BusiessA

- (void)handleTask {
    ///在调用父类之前可以处理一些逻辑;
    
    ///调用父类实现
    [super handleTask];
    
    ///在调用父类之后仍然可以处理一些逻辑;
}
@end
///桥接模式
- (void)bridge {
    ///根据业务情形选择使用BusiessA或者BusiessB
    BusiessModel *businessM = [[BusiessA alloc] init];
    ///根据业务情形选择使用Request1或者Request2
    RequestModel *requestM = [[Request2 alloc] init];
    ///抽象类的逻辑实例实现
    businessM.requestModel = requestM;
    ///真正处理业务
    [businessM handleTask];
}

桥接-示例代码

2.3 适配器模式;

适用场景: 工程中的年代很久远的类, 并且逻辑已经很成熟, 基本上没什么问题, 如果要对其更改或者扩展, 直接更改是不合适的, 这时通过适配器模式就行扩展比较合适;
代码讲解示例

#一个年代很久远的类, 功能逻辑已经很完善;
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface RemoteModel : NSObject

///这个类的其中一个功能, 已经相对很完善
- (void)operationTask;

@end
NS_ASSUME_NONNULL_END

#.m实现
#import "RemoteModel.h"
@implementation RemoteModel

///这个类的其中一个功能, 已经相对很完善
- (void)operationTask {
    NSLog(@"处理一些逻辑");
}

@end

对其原先逻辑进行扩展


#import <Foundation/Foundation.h>
#import "RemoteModel.h"
NS_ASSUME_NONNULL_BEGIN
@interface NowModel : NSObject

/**
 现在想对RemoteModel中 - (void)operationTask; 方法进行添加一些新的逻辑;
 因为原来的逻辑已经很完整,完善; 通过适配器模式进行扩展;
 */
@property (nonatomic, strong) RemoteModel   *remoteM;
///对原先逻辑进行扩展
- (void)nowOperationTask;

@end
NS_ASSUME_NONNULL_END

#.m实现
#import "NowModel.h"
@implementation NowModel

///对原先逻辑进行扩展
- (void)nowOperationTask {
    NSLog(@"先执行添加的新逻辑");
    ///然后执行原先的逻辑
    [self.remoteM operationTask];
    NSLog(@"然后执行一些补充的新逻辑");
}

@end
///适配器模式
- (void)adapter {
    RemoteModel *remoteModel = [[RemoteModel alloc] init];
    NowModel *nowModel = [[NowModel alloc] init];
    nowM.remoteM = remoteModel;
    [nowModel nowOperationTask];
}

适配器-示例代码

2.4 单例模式;

使用场景, 工程中一些常用的逻辑可以放在单例中, 这样可以快速获取到; 因为单例整个生命周期只会创建一次, 节省资源;

代码示例讲解

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN
///遵循这两个协议是为了防止无意中进行了copy或mutableCopy而开辟新地址;
@interface GlobalModel : NSObject<NSCopying, NSMutableCopying>

+ (GlobalModel *)share;

@end
NS_ASSUME_NONNULL_END



#.m实现
#import "GlobalModel.h"
@implementation GlobalModel

+ (GlobalModel *)share {
    static GlobalModel * model = nil;
    ///确保只能执行一次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        ///为什么不能用self进行alloc, 因为重写下面的方法中返回了[self share]会造成循环;
        model = [[super allocWithZone:NULL] init];
    });
    return model;
}

///确保多次alloc也是同一块地址
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    return [self share];
}

///确保即使进行copy了也是同一块地址
- (id)copyWithZone:(NSZone *)zone {
    return  self;
}

///确保即使进行mutableCopy了也是同一块地址
- (id)mutableCopyWithZone:(NSZone *)zone {
    return self;
}
@end

///单例模式
- (void)singleton {
    GlobalModel *model = [[GlobalModel alloc] init];
    GlobalModel *model1 = [[GlobalModel alloc] init];
    GlobalModel *model2 = [model copy];
    GlobalModel *model3 = [model mutableCopy];
    GlobalModel *model4 = [GlobalModel share];
    NSLog(@"model地址:   %@", model);
    NSLog(@"model1地址1: %@", model1);
    NSLog(@"model2地址2: %@", model2);
    NSLog(@"model3地址3: %@", model3);
    NSLog(@"model4地址4: %@", model4);
}
#不论是几次alloc或者copy, mutableCopy始终是同一块内存地址

2019-05-06 17:19:42.822105+0800 DesignMode[13036:251989] model地址:   <GlobalModel: 0x60000174c020>
2019-05-06 17:19:42.822246+0800 DesignMode[13036:251989] model1地址1: <GlobalModel: 0x60000174c020>
2019-05-06 17:19:42.822400+0800 DesignMode[13036:251989] model2地址2: <GlobalModel: 0x60000174c020>
2019-05-06 17:19:42.822511+0800 DesignMode[13036:251989] model3地址3: <GlobalModel: 0x60000174c020>
2019-05-06 17:19:42.822599+0800 DesignMode[13036:251989] model4地址4: <GlobalModel: 0x60000174c020>

单例-示例代码



参考文章
推荐 面向对象设计原则概述
面向对象设计原则之单一职责原则
面向对象设计原则之开闭原则
面向对象设计原则之接口隔离原则
面向对象设计原则之依赖倒置原则
面向对象设计原则之里氏替换原则
面向对象设计原则之迪米特法则
面向对象设计原则之合成复用原则
iOS 设计模式详解

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 230,501评论 6 544
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,673评论 3 429
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 178,610评论 0 383
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,939评论 1 318
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,668评论 6 412
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 56,004评论 1 329
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 44,001评论 3 449
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 43,173评论 0 290
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,705评论 1 336
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,426评论 3 359
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,656评论 1 374
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 39,139评论 5 364
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,833评论 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 35,247评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,580评论 1 295
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,371评论 3 400
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,621评论 2 380