Decorate Pattern装饰者模式-- iOS继承与代理

场景

写这篇文章的背景是在看casatwy的网络层架构代码时对子类继承和协议代理方法的实现产生了疑惑,进行了探索。

问题

问题情景:父类的方法列表中和协议中有同样的方法(代码如下),子类继承方法同时代理方也实现协议方法,那么当父类调用decoratePatternTest这个方法时具体的执行方是谁?子类同时是代理方和不是代理方分别有什么样的情况呢?


#import <Foundation/Foundation.h>
@protocol BaseDecorateDelegate <NSObject>
- (NSString *)decoratePatternTest;
@end
@interface BaseProtocolTest : NSObject
@property (nonatomic, weak) id<BaseDecorateDelegate> decorate;
- (NSString *)decoratePatternTest;
- (void)startTest;
@end

casatwy注释

在casatwy的代码中遇到上述问题的有BaseAPIManager拦截器代码部分,作者的注释如下:

/*
    拦截器的功能可以由子类通过继承实现,也可以由其它对象实现,两种做法可以共存
    当两种情况共存的时候,子类重载的方法一定要调用一下super
    然后它们的调用顺序是BaseManager会先调用子类重载的实现,再调用外部interceptor的实现
    
    notes:
        正常情况下,拦截器是通过代理的方式实现的,因此可以不需要以下这些代码
        但是为了将来拓展方便,如果在调用拦截器之前manager又希望自己能够先做一些事情,所以这些方法还是需要能够被继承重载的
        所有重载的方法,都要调用一下super,这样才能保证外部interceptor能够被调到
        这就是decorate pattern
 */
 

注释中提到:

  • 子类继承和代理方实现协议可以共存
  • 两种共存时子类重载的方法必须要调用父类方法
  • 两者调用顺序是先实现子类重载的方法再调用代理方实现的方法
  • 最后提到的是 Decorate Pattern 装饰者模式

分情况实现验证

  1. 子类继承父类方法 & 子类没有遵守父类协议 & 其他类遵守协议实现代理方法
  2. 子类继承父类方法 & 遵守父类协议

父类方法:


- (NSString *)decoratePatternTest {
    NSLog(@"执行父类decoratePatternTest");
    NSString *string = @"基类中实现decoratePatternTest";
    if (self != self.decorate && [self.decorate respondsToSelector:@selector(decoratePatternTest)]) {
       string = [self.decorate decoratePatternTest];
    }
    return string;
}

子类方法:


- (NSString *)decoratePatternTest {
    [super decoratePatternTest];
    NSLog(@"子类执行decoratePatternTest");
    return @"子类中实现decoratePatternTest";
}

非子类代理者方法:


- (NSString *)decoratePatternTest {
    NSLog(@"执行过代理方实现decoratePatternTest");
    return @"代理方实现decoratePatternTest";
}

测试结果:

父类 子类 Other 父类 子类 Other
遵守协议实现方法 - N Y - Y N
继承方法 - Y N - Y N
方法执行顺序 2 1 3 2 1 -

关键

其实问题的关键是在代理者的协议方法调用的时机。

  • 当子类继承了方法又是代理者,那么在父类方法中通过(self != self.decotate)来判断不用再去执行协议中的方法

  • 子类不是代理者没有继承方法,则在父类方法中调用代理者执行协议方法

  • 子类不是代理者但是继承了方法,父类调用代理者协议方法

    • 两个方法的调其顺序是 子类中方法 -> 父类中方法 -> 代理者方法
    • 两个方法中的有效代码执行顺序要看 子类方法中 super调用的位置 (super调用在子类有效代码前则 代理者方法的代码先执行)------->见最后一节Decorate Pattern 装饰者模式

代码中不用调用父类方法的情况

作者的代码BaseAPIManager中的reformerParams方法在父类实例方法和协议方法中存在,但是子类继承实现时并不需要调用父类的 reformerParams,原因是父类中作了如下处理:


- (instancetype)init
{
    self = [super init];
    if (self) {
        if ([self conformsToProtocol:@protocol(BaseProtocolDelegate)]) {
            self.child = (id <BaseProtocolDelegate>)self;
        } else {
            NSException *exception = [[NSException alloc] init];
            @throw exception;
        }

    }
    return self;
}

- (NSString *)reformerParams {
    NSLog(@"执行父类reformerTest");
    IMP childIMP = [self.child methodForSelector:@selector(reformerTest)];
    IMP selfIMP = [self methodForSelector:@selector(reformerTest)];
    
    if (childIMP == selfIMP) {
        return @"子类没有继承此方法";
    } else {
        // 如果child是继承得来的,那么这里就不会跑到,会直接跑子类中的IMP。
        // 如果child是另一个对象,就会跑到这里
        NSString *result = nil;
        result = [self.child reformerParams];
        if (result) {
            return result;
        } else {
            return @"default";
        }
    }
}
  • 初始化方法中首先保证子类必须遵守了BaseProtocolDelegate协议

  • 子类实现了reformerParams方法并没有调用super并不会出发父类的reformerParams

  • 根据消息传递机制父类reformerParams 方法会被调用说明子类没有实现此方法,那么执行非子类代理者的协议方法

  • 父类要求子类必须遵守协议,那么此协议BaseProtocolDelegate更多的用处是获取子类对象

Decorate Pattern 装饰者模式

装饰者模式动态地将责任附件到对象上,若要扩展功能,装饰着提供了比继承更具有弹性的方案。

装饰者模式的设计原则:


可以随心为一个类扩展功能,但不允许对已经存在的代码进行修改。

装饰者模式的主要特点:

装饰者模式示意图:

实际的例子:java中常用的java.io类就存在着大量装饰者

以上内容摘自《Head First设计模式》 第三章 装饰者模式 (79-107)

对应到iOS中本文的场景:

被装饰者 功能组件 装饰者
委托方父类 代理方OtherObject(非子类) 子类

其实在思考装饰者模式对应情况上,有点疑惑: 装饰者类Decorator没有找到对应的类

但装饰者模式中的关键点:

装饰者可以在被所委托被装饰者的行为之前或之后,加上自己的行为,以达到特定目标。

在本文的例子中有明显体现:子类继承的方法中调用super方法,在这之前或者之后可以加上自己的行为,达到特定目标。

对装饰者模式的理解还比较基础有待继续研究……欢迎指正指导,谢谢!


iOS装饰模式的继续研究请看我的新文章Objective-C中的装饰模式
小胡子博客:冷读空间

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

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,560评论 18 399
  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 3,894评论 1 15
  • Iterator模式 (迭代器) 一个一个遍历 一个集合类可以遵守 Iterator 协议,并实现一个 Itera...
    SSBun阅读 1,824评论 0 15
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 我一直想叫朋友加入团队,但是他没进来,原因有二,其一,身体受不了,其二,自己学习时间安排不到。昨天他同我说,他有个...
    瘦砖头阅读 1,231评论 1 4