最近在研究RAC,发现编程思想这一个很有趣且有用的东西,就大概研究了一下“链式编程”、“函数式编程”、“响应式编程”几种主要的编程思想,发现对提高代码质量和业务水平很有帮助,下面就挨个介绍一下,也可以直接看我的DEMO
链式编程
思想特点:
是将多个操作(多行代码)通过点号(.)链接在一起成为一句代码,使代码可读性好。类似a(1).b(2).c(3),方法的返回值是block,block必须有返回值(本身对象),block参数(需要操作的值)
代表:
masonry框架、SDAutoLayout框架。
demo:
TJChainedModeClass *chainedMode1 = [TJChainedModeClass new];
chainedMode1.addName(@"jackson").addNickname(@"josh").addAge(5).addHeadImg(nil);
其中
chainedMode1.addName(@"jackson").addNickname(@"josh").addAge(5).addHeadImg(nil);
每一步点操作都会返回一个TJChainedModeClass实例对象,这样就可直接调取下一个点属性操作
这里贴出一个点操作函数,其他的都是类似形式
typedef TJChainedModeClass *(^configerString)(NSString *string);//这里的configerString是在.h文件中定义的返回值为本类对象的一个block,放这里方便查看
-(configerString)addName{
if (!_addName) {
__weak typeof(self) weakSelf = self;
return ^(NSString *string){
weakSelf.name = string;
return weakSelf;
};
}
return _addName;
}
响应式编程
思想特点:
不需要考虑调用顺序,只需要知道考虑结果,类似于蝴蝶效应,产生一个事件,会影响很多东西,这些事件像流一样的传播出去,然后影响结果,借用面向对象的一句话,万物皆是流。是把操作尽量写成一系列嵌套的函数或者方法调用。
代表:
KVO运用、ReactiveCocoa(也做函数响应式编程)。
demo
这里我选用KVO的方式来实现,其实方法很多,可以参考RAC。我这里是给本类的reactNum添加属性监听,有改变外部调用的地方也会执行block,而另外一个属性normalNum的改变则不会引起这种改变
.h中代码:
@property (nonatomic, assign) int reactNum;
@property (nonatomic, assign) int normalNum;
@property (nonatomic, copy) void (^reactBlock)(int num1,int num2);
.m实现:
-(instancetype)init{
self = [super init];
[self addObserver:self forKeyPath:@"reactNum" options:NSKeyValueObservingOptionNew context:nil];
return self;
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
self.reactBlock(self.normalNum,self.reactNum);
}
调用:
-(void)reactProgramming{
//类中的reactNum数值发生变化时,会调用打印方法
TJReactiveProgrammingClass *reactObj = [TJReactiveProgrammingClass new];
_reactObj = reactObj;
//[reactObj TJ_addObserver:self forKeyPath:@"reactNum" options:NSKeyValueObservingOptionNew context:nil];
reactObj.reactBlock = ^(int normalNum,int reactNum){
NSLog(@"normalNum:%d----reactNum:%d",normalNum,reactNum);
};
}
//点击测试效果
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
_reactObj.reactNum = arc4random() % 100;//改变时会响应
//_reactObj.normalNum = arc4random() % 100;//改变时不会响应
}
函数式编程
什么是函数式编程?函数式编程在iOS中是借由block实现的,通过声明一个block,类似于定义了一个“函数”,再将这个“函数”传递给调用的方法,以此来实现对调用该方法时中间一些过程或者对结果处理的“自定义”,而其内部的其他环节完全不需要暴露给调用者。实际上,调用者也根本不需要知道。
咱们来看一个例子:
[self.playBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(self.picView);
make.width.height.mas_equalTo(50);
}];
从中可以看出,Masonry中实现自动布局的关键类MASConstraintMaker,他的实例并不是调用者自己创建的,而是通过调用方法mas_makeConstraints:(这里是用了Category),其参数block中的参数传递过来的。
也就是说,通过mas_makeConstraints:这个方法,我们不需要知道MASConstraintMaker的实例是怎么创建的,也不需要知道具体是怎么实现了给View添加了自动布局,唯一需要的是实现(传递)一个block,在block里按照Masonry的方式指定需要添加的约束就可以了。
这种实现方式就是本篇要说的函数式编程。
函数式编程本质:
就是往方法中传入Block,方法中嵌套Block调用,把代码聚合起来管理
函数式编程特点:
每个方法必须有返回值(本身对象),把函数或者Block当做参数,block参数(需要操作的值)block返回值(操作结果)
代表:
Masonry、ReactiveCocoa(也做函数响应式编程)。
demo:
现在有个类,它有一个NSString *finalString
属性,我不想暴露给外部,在.h文件中我定义一个能传入外部blick的方法,和一个可以操作这个属性值的方法(可能类的内部需要这种操作,但又不想把属性暴露)
.h代码:
+ (NSString *)makeTool:(void(^)(TJFunctionalProgrammingClass *tool))block;
typedef TJFunctionalProgrammingClass *(^Function)(NSArray<NSString *> *strings);
@property (nonatomic, strong, readonly) Function FunctionalAction;
.m加入一个私有属性:
@property (nonatomic, copy) NSString *finalString;
.m方法实现:
+(NSString *)makeTool:(void (^)(TJFunctionalProgrammingClass *tool))block{
if (block) {
TJFunctionalProgrammingClass *tool = [[TJFunctionalProgrammingClass alloc]init];
block(tool);
return tool.finalString;
}
return nil;
}
-(Function)FunctionalAction{
__weak typeof(self) weakself = self;
return ^(NSArray<NSString *> *strings ){
if (strings.count>0) {
weakself.finalString = [NSString stringWithFormat:@"FunctionalProgramming + %@",[strings componentsJoinedByString:@"-"]];
}else{
weakself.finalString = @"FunctionalProgramming + nil";
}
return weakself;
};
}
调用:
NSString *str = [TJFunctionalProgrammingClass makeTool:^(TJFunctionalProgrammingClass *tool) {
tool.FunctionalAction(@[@"张三",@"李四",@"王五"]);
}];
NSLog(@"functionalProgramming----%@",str);