Adapter+ResponderChain+Strategy避免UITableView重写大量delegate以及n多if-else判断和太多Block

前言

近来换工作,接手一项目,不知如何评价,有优点(一些新技术都要加上去,估计多半只是前前同事拿来练手),缺点也格外明显。最明显的缺点是90%的代码堆积在UIViewController中,相信不用多说,各位大佬就秒懂。这样的代码中更多的是if-else堆积,每个if-else判断中 200~1000行代码不等,一个UIViewController中 均值 15个if-else判断 , 先替自己默哀一分钟

为了避免n多if-else看的晕头转向,针对使用频率比较高和容易产生大量if-else的UITableView,用Adapter模式+ResponderChain+Strategy模式 来避免每次都要重写UITableView的delegate和dataSource 和在点击cell以及点击cell中控件时造成n个if-else。(Adapter模式是之前一直用的写法,因为我是个“懒人”,代码一行完成的不写两行,所以前期一直用Adapter模式。至于ResponderChain和Strategy模式 是参考了casa的文章一种基于ResponderChain的对象交互方式后与Adapter结合)。Example已经托管到全球最大男性交友网站Github点击cloneOrdown(也是最近才听说这个别称,好吧,已经被曾经虐我成渣渣的微软收购),基于更好的理解这三种组合的综合使用,代码中没有按照传统的MVC或MVVM来写,希望大家能更好的理解这些模式和组合,因为任何一个都可以单独用于不同的场景。

一、Adapter模式 适配UITableView、UITableViewDelegate、UITableViewDataSource

Adapter模式也是常用的设计模式之一,主要是在两个类或协议之间起到桥梁的作用,具体的官方定义大家可以查阅其它资料。Adapter模式主要有三块内容:AdapterAdapteeTarget,Adapter就是适配器,起到中间桥接作用;Adaptee被适配者,Target是适配对象,Adapter的桥接作用就是将把Adaptee适配到Target。如果只是针对单一Adaptee类将其适配到Target称为类对象适配,针对适配多个Adaptee以及其子类,称之为对象适配器。 之前有看《Objective-C编程之道》书中提到了该模式,但是中文翻译版本估计是外行翻译的,读起来不太通顺,建议看英文原版。

接下来简单用图示来说明AdapterUITableView以及其delegatedatasource 之间的关系

Adapter三者关系

简单来讲Adapter起到的作用就是将UITableDelegate和UITableView原来两个不同的目标能和谐的在一起工作,这一步就是将原来我们可能会在View、自定义的TableView 或者像接手项目中ViewController中进行的设置都交给了Adapter来适配。在此关系中,UITableView就是Adaptee(被适配者),我们通过Adapter 将UITableViewDelegate(在此关系中是Target)两者结合起来能够顺利地运行。

到此可以简单理解Adapter将UITableViewDelegate和UITableView结合的这种做法,那么如何给我们的Adaptee 即我们的UITableView都被适配上这一Target呢 ,就通过OC中的类目进行设置

- (void)setAdapter:(UITableViewAdapter *)adapter
{
    [self setDelegate:adapter];
    [self setDataSource:adapter];
    [adapter setView:self];
    [self reloadData];
}

这样,在项目中只要将创建的UITableView与Adapter关联便可避免每次重写大量代理方法。

任何一种模式的应用都有其局限性,Adapter模式下有些方法还是不可避免的要重写,如果复杂的页面需要重写的代理方法会增多,所以如果大家要用这种模式,Adapter中尽量考虑多种情况。

另外,Adapter模式下仍旧不可避免会声明出一些Block以供外界调用,如何解耦呢?

二、基于ResponderChain传递点击事件
在此,解耦的出发点是考虑如何消除类与类之间的相互引用即可让事件传递出去。iOS中继承自UIResponse的控件会形成一棵响应树,顶层是UIApplication。所有在这棵响应树上的控件事件可以通过其中的一条线路传递到顶层。结合这一特性,我们便可将原来声明的Block砍掉,换成响应事件传递。要想事件沿着整个Responder传递,我们同样需要给UIResponse统一设置

- (void)rountEvent:(NSString *)eventName userInfo:(NSDictionary *)userInfo
{
    [[self nextResponder] rountEvent:eventName userInfo:userInfo];
}

在原来需要传递Block或设置代理的地方添加rountEvent:(NSString *)eventName userInfo:(NSDictionary *)userInfo,比如在cell的点击事件中添加

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    
    [_tableView rountEvent:kCCellSeletedEventName userInfo:@{@"indexPath":indexPath}];
    
}

这种传递模式可以应用到我们平常使用的其它模式或场景中。

三、Strategy模式避免if-else

策略模式通常把一个系列的算法包装到一系列的策略类里面,这里我们包装的不是算法,而是NSInvocation 提到NSInvocation研究过消息转发机制的同学应该对此并不陌生,为什么选择包装NSInvocation,因为我们最终会回归到方法事件调用。如果没有研究过消息转发机制,那么大家对这行代码相比熟悉的不能再熟悉了

[button addTarget:self action:@selector(<#selector#>) forControlEvents:UIControlEventTouchUpInside]

这行代码实际上最终会创建一NSInvocaion,明白了为何我们要包装的是NSInvocation之后,接下来可以选择根据不同的eventName创建不同的NSInvocation

- (NSDictionary *)strategyDictionary{
    
    NSDictionary *strategyDictionary = @{kCCellSeletedEventName:[self createInvocationWithSeletor:@selector(jumpToController:)]};
    return strategyDictionary;
    
}

将点击Cell对应的NSInvocation放在一个字典中,在控制器处理事件时再根据不同的事件名称取出对应的NSInvocation分别执行

- (void)rountEvent:(NSString *)eventName userInfo:(NSDictionary *)userInfo
{
    NSInvocation *invocation = [self strategyDictionary][eventName];
    [invocation setArgument:&userInfo atIndex:2];
    [invocation invoke];
}

这样在点击Cell的时候我们定义的方法就会执行了,如果事件处理增多,那么可以在字典中增加相应的NSInvocation

至于创建NSInvocation,可以单独声明出去

- (NSInvocation *)createInvocationWithSeletor:(SEL)seletorname
{
    
    NSInvocation *invocaion = [NSInvocation invocationWithMethodSignature:[[self class] instanceMethodSignatureForSelector:seletorname]];
    [invocaion setTarget:self];
    [invocaion setSelector:seletorname];
    return invocaion;
    
}

基于ResponserChain的交互方式还可以结合结合其它模式,大家可以去casa的博客中看。

得益于这两种模式,一种传递方式,改了接手项目很多冗余的代码和大量的if-else,写成博文,希望对大家有所帮助。

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

推荐阅读更多精彩内容

  • 1 场景问题# 1.1 装配电脑的例子## 旧的硬盘和电源 小李有一台老的台式电脑,硬盘实在是太小了,仅仅40GB...
    七寸知架构阅读 3,244评论 5 60
  • 我的笔记本电脑的工作电压是20V,而我国的家庭用电是220V,如何让20V的笔记本电脑能够在220V的电压下工作?...
    justCode_阅读 1,423评论 0 5
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,094评论 1 32
  • 我的笔记本电脑的工作电压是20V,而我国的家庭用电是220V,如何让20V的笔记本电脑能够在220V的电压下工作?...
    justCode_阅读 730评论 0 1
  • 戒烟第三天。 本次戒烟无外部强制因素,属于无捆绑安装的纯主动戒烟,不选黄道吉日,也无任何仪式性的东西,妻女远在千里...
    王立平阅读 590评论 9 9