0x01 What?
类似车间流水线的模式,各环节依次处理好自己的事.
工程中常用于拦截器的设计.
有单程链,往返链,
1.0 优缺点
- 优点:
- 每个环节专注做好自己的事
- 很容易新增/替换/删除节点,交换节点顺序
- 缺点:
- 一件完整的事务被细分为多个环节,增加了各环节的维护成本
- 代码调试不容易,因为可以会出现循环递归调用
0x02 How?
场景一:
评论系统要屏蔽一些用户回复,比如一些敏感字眼, HTML标签, 还有一些其它的规则
完整代码请在Github
DesignPatterns/DesignPatterns/FilterChain/TextFilter/*
DesignPatterns/DesignPatternsTests/FilterChainTests.m
step1: 创建过滤屏蔽协议
1 | FOUNDATION_EXTERN NSString *const kHTMLTag_OLD; |
step2: 创建各种需要的过滤器1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//HTML标签过滤器
@interface HTMLFilter : NSObject<StringFilter>
@end
@implementation HTMLFilter
@synthesize filteRules;
- (instancetype)init{
if (self = [super init]) {
filteRules = [NSMutableArray array];
}
return self;
}
- (void)addFilteRule:(NSDictionary *)rule{
[self.filteRules addObject:rule];
}
- (NSString *)filterString:(NSString *)aString{
for (NSDictionary *rule in self.filteRules) {
NSString *old = rule[kHTMLTag_OLD];
NSString *replaced = rule[kHTMLTag_NEW];
aString = [aString stringByReplacingOccurrencesOfString:old withString:replaced];
}
return aString;
}
@end
//敏感字过滤器
@interface SensitiveFilter : NSObject<StringFilter>
@end
@implementation SensitiveFilter
- (void)addFilteRule:(NSDictionary *)rule{
[self.filteRules addObject:rule];
}
- (NSString *)filterString:(NSString *)aString{
for (NSDictionary *rule in self.filteRules) {
NSString *old = rule[kHTMLTag_OLD];
NSString *replaced = rule[kHTMLTag_NEW];
aString = [aString stringByReplacingOccurrencesOfString:old withString:replaced];
}
return aString;
}
@end
//其它过滤器
@interface OtherFilter : NSObject<StringFilter>
@end
step3: 创建响应链条1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//StringFilterChain.h
//由若干个环节组成一个链条,每个环节实现同一协议
@interface StringFilterChain : NSObject
//添加过滤器
- (void)addFilter:(id<StringFilter>)filter;
//添加过滤器
- (StringFilterChain* (^)(id<StringFilter> filter))addFilter;
//执行过滤任务
- (NSString *)doFilterFor:(NSString *)aString;
@end
//StringFilterChain.m
- (NSString *)doFilterFor:(NSString *)aString{
for (id<StringFilter> filter in self.filters) {
aString = [filter filterString:aString];
}
return aString;
}
step4: 构建响应链对象并使用1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33StringFilterChain *chain = [[StringFilterChain alloc] init];
// id<StringFilter> filter1;
// id<StringFilter> filter2;
// id<StringFilter> filter3;
//
{
HTMLFilter *filter = [[HTMLFilter alloc] init];
[filter addFilteRule:makeFilterRule(@"<", @"[")];
[filter addFilteRule:makeFilterRule(@">", @"]")];
[filter addFilteRule:makeFilterRule(@"&", @" and ")];
[chain addFilter:filter];
// filter1 = filter;
}
{
SensitiveFilter *filter = [[SensitiveFilter alloc] init];
[filter addFilteRule:makeFilterRule(@"共产党", @"伟大领袖")];
[filter addFilteRule:makeFilterRule(@"敏感", @"不敏感")];
[filter addFilteRule:makeFilterRule(@"中国男足", @"男猪")];
[chain addFilter:filter];
// filter2 = filter;
}
{
OtherFilter *filter = [[OtherFilter alloc] init];
[chain addFilter:filter];
// filter3 = filter;
}
// chain.addFilter(filter1).addFilter(filter2).addFilter(filter3);
NSString *result = [chain doFilterFor:@"<真是太敏感了> & 共产党万岁, other ..中国男足"];
NSString *expected = @"[真是太不敏感了] and 伟大领袖万岁, 其它的过滤器 ..男猪";
XCTAssertTrue([result isEqualToString:expected]);
场景二:
对网络请求添加各种前置后置处理(黑白名单,参数校验,安全签名加密等)
完整代码请在Github
DesignPatterns/DesignPatterns/FilterChain/NetworkingFilter/*
DesignPatterns/DesignPatternsTests/FilterChainTests.m
step1: 创建协议
1 | NS_INLINE NSError* makeFilterError(NSUInteger errorCode, NSString *desc){ |
step2: 创建各种需要的过滤器
1 |
|
step3: 创建响应链条1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//NetworkingFilterChain.h
@interface NetworkingFilterChain : NSObject<NetworkingFilter>
- (NetworkingFilterChain* (^)(id<NetworkingFilter> filter))addFilter;
@end
//NetworkingFilterChain.m
@interface NetworkingFilterChain()
@property (nonatomic,strong) NSMutableArray<id<NetworkingFilter>> *filters;
@property (nonatomic,assign) NSUInteger index;
@end
@implementation NetworkingFilterChain
- (instancetype)init{
if (self = [super init]) {
_filters = [NSMutableArray array];
}
return self;
}
- (NetworkingFilterChain *(^)(id<NetworkingFilter>))addFilter{
return ^(id<NetworkingFilter> filter){
[self.filters addObject:filter];
return self;
};
}
- (void)doFilterForRequest:(FTURLRequest *)request
response:(FTURLResponse *)response
chain:(NetworkingFilterChain *)chain
error:(NSError *__autoreleasing *)error{
if (*error) {
self.index = 0;
return;
}
if (self.index == self.filters.count) {
self.index = 0;
return;
}
id<NetworkingFilter> filter = self.filters[self.index++];
[filter doFilterForRequest:request
response:response
chain:self
error:error];
}
@end
step4: 构建并使用1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33//FilterChainTests.m
- (void)setUp {
[super setUp];
self.networkingChain = [[NetworkingFilterChain alloc] init];
self.networkingChain.addFilter([NotFoundFilter new]);
self.networkingChain.addFilter([ParameterFilter new]);
self.networkingChain.addFilter([NetworkingSecretFilter new]);
self.request = [[FTURLRequest alloc] init];
self.request.requestMethod = FTRequestMethod_GET;
self.request.domain = @"https://api.github.com";
self.request.interface = @"/orgs/octokit/repos";
[self.request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[self.request addParam:@"finderTiwk" forKey:@"usrename"];
[self.request addParam:@(1514739661000) forKey:@"timestamp"];
self.response = [[FTURLResponse alloc] init];
self.response.statusCode = 200;
self.response.retData = @{
@"code":@"0",
@"result":@{
@"name":@"FinderTiwk",
@"age":@"28",
@"email":@"136652711@qq.com",
}
};
NSError *error;
[self.networkingChain doFilterForRequest:self.request
response:self.response
chain:self.networkingChain
error:&error];
XCTAssertNil(error);
}
0x03 iOS系统中的响应链设计模式的应用
事件传递响应机制
1. 事件传递
UIApplication–>UIWindow–>递归找到最适配的事件接收者(遍历视图子控件时,从后向前,因为后加上来的控件在最上面)1
2
3
4
5
6
7
8
9
10/*
UIView不能接收触摸事件的三种情况:
不接受用户交互:userInteractionEnabled = NO;
隐藏:hidden = YES;
透明:alpha = 0.0~0.01
*/
//通过这两个方法来寻找能响应事件的控件
- hitTest:withEvent:
- pointInside: withEvent:
2. 事件响应
最适合的控件——>扔给父控制直到最上层控件 —-> 最终到UIApplication,如果处理不了的话事件丢弃