可能被忽略掉編程技巧 之 鏈式編程
@author Jou Email Weibo or Github
可能被忽略掉編程技巧 之 鏈式編程
預熱 - 鏈式編程
哎呦叉抡,不錯哦。有點時間袁稽, 隨便寫篇文章,聊聊iOS中的鏈式編程溅呢。
鏈式編程披蕉? 如果你不是iOS開發(fā)者,相信你應該沒聽過鏈式編程伞访。對的掂骏, 鏈式編程,并不是一種編程范式厚掷。鏈式編程這種表述弟灼, 應該是在Objective-C獨有,因為Objective-C是一門繁瑣而奇怪的語言冒黑。
在Swift之前田绑,想成為iOS開發(fā)者,在入門前你就有了兩個門檻: 1. 你要有臺蘋果抡爹, 2. 你要肯于接受Objective-C的反人類掩驱,和繁瑣。
所以形式上冬竟,鏈式編程是"點方法"的調用欧穴,來彌補Objc的繁瑣。
Objective-C
NSString objc = @"Hello world";
[objc lowercaseString]; // 給objc 發(fā)送lowercaseString的消息
而相比之下swift 就更為簡潔
let str = "Hello, playground"
str.lowercaseString;
鏈式編程 - 點式調用 LinkBlock
為了實現(xiàn)Objc中的點式調用泵殴, Novo已經(jīng)有了一個基于block的好玩的實現(xiàn)LinkBlock涮帘。
LinkBlock 旨在幫助我們實現(xiàn)點方法調用的happy path。
如:
UIButton* btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.frame= CGRectMake(20, 20, 150, 80);
UIColor *color = [UIColor colorWithRed:255/255.0 green:22/255.0 blue:150/255.0 alpha:1.0];
btn.backgroundColor = color;
[btn setTitle:@"click change color" forState:UIControlStateNormal];
[self.view addSubview:btn];
//如果使用鏈式編程的方式笑诅,大部分工作可以在思路連續(xù)的情況下進行
//now just using one line.Most work can be wrapped up in the idea of ??ongoing cases
btn.viewSetFrame(20,20,150,80).viewBGColor(@"0xff22cc".strToColorFromHexStr())
.viewAddToView(self.view).btnTitle(@"click change color", UIControlStateNormal);
是不是簡潔了許多调缨。
鏈式編程 - Masonry
自從iOS設備尺寸多樣化以后疮鲫,為了適配大小屏,我們用到了autolayout弦叶。
即便是我們使用了VFL俊犯,官方的autolayout寫法,在代碼實現(xiàn)上也過于繁瑣湾蔓。
所以很大程度上, 一部分人轉而重用storyboard界面布局砌梆,一部分轉而重度依賴mansory默责, 還有大多數(shù)在結合使用。
而Masonry的實現(xiàn)上咸包, 恰恰就是圍繞Maker對象的鏈式編程桃序。
之前autolayout的代碼是這樣的
UIView *superview = self;
UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[superview addConstraints:@[
//view1 constraints
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:padding.left],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-padding.bottom],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeRight
multiplier:1
constant:-padding.right],
]];
Masonry實現(xiàn)、
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
make.left.equalTo(superview.mas_left).with.offset(padding.left);
make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];
顯而易見的烂瘫、真是簡潔到?jīng)]朋友媒熊。
ps: Masonry 在github上 已有1W+的star。
除了形式上的鏈式調用坟比, Objc中所談的鏈式調用芦鳍, 更是函數(shù)編程中一種常用的編程手段。
函數(shù)式編程 - Function Chaining
在swift中引入了函數(shù)式編程之后葛账,把函數(shù)編程推到了新的高度柠衅。
而在swift中, 偶爾會見到這樣的代碼籍琳。
let result = students |> filterOutFailed |> sortByGrade |> formatForPrint |> joinByComma
把 ‘|>’ 換成 ’.' 菲宴,便有了極大的相似并不是偶然, 鏈式編程趋急,只是函數(shù)編程思想的一種結合喝峦。
百分百的知識 - 實踐 鏈式編程
如果團隊有人在項目中,沒有討論的貿(mào)然加入了LinkBlock框架呜达, 我肯定還是會argue一下的谣蠢。
因過于依賴于LinkBlock,在代碼可讀性上增加了難度查近,并且增加了不可預期的崩潰漩怎。
如LinkBlock中不能很好的處理,給nil進行鏈式調用嗦嗡,造成崩潰勋锤。
所以我認為,鏈式編程更好的實踐侥祭,應該是應用在復用性較好的組件中, 最好還可以像masonry一樣叁执,使用block包裹起來茄厘。
如, 語句塊在objc的使用方式之一:
self.tableView.tableFooterView = ({
UIView *view = [[UIView alloc]initWithFrame:CGRectZero];
view;
});
這不是純秀技巧, 在代碼可讀性上谈宛, 他的模塊次哈, 和邏輯更加清晰。
所以我最近在考慮吆录,將鏈式編程的思想加入我自己的 開源項目Router窑滞, 或 Dispatcher 中。 而形式上恢筝,則更類似Masonry的maker哀卫。
場景是 : 假如,我買了一架灰機撬槽, 我要駕駛它此改。
使用Block的特性,Trick實現(xiàn)鏈式調用
那么侄柔,我的飛機共啃,一定可以起飛,降落暂题, 上下左右移剪,依照block的特性。我可以這么實現(xiàn)它
- (MAFlight *(^)(CGFloat distence))takeOff
{
return ^MAFlight *(CGFloat distence)
{
//Do move distence
return self;
};
}
- (MAFlight *(^)(CGFloat distence))landDown
{
return ^MAFlight *(CGFloat distence)
{
//Do move distence
return self;
};
}
- (MAFlight *(^)(CGFloat distence))forward
{
return ^MAFlight *(CGFloat distence)
{
//Do move distence
return self;
};
}
- (MAFlight *(^)(CGFloat distence))back
{
return ^MAFlight *(CGFloat distence)
{
//Do move distence
return self;
};
}
- (MAFlight *(^)(CGFloat distence))left
{
return ^MAFlight *(CGFloat distence)
{
//Do move distence
return self;
};
}
- (MAFlight *(^)(CGFloat distence))right
{
return ^MAFlight *(CGFloat distence)
{
//Do move distence
return self;
};
}
所以我們就可以這么調用
MAFlight *flight = [[MAFlight alloc]init];//我買了飛機
flight.takeOff(10.f).forward(100000.f).left(10.f).right(1.f).back(1.f).back(1.f).landDown(10.f);
好了那么我們已經(jīng)實現(xiàn)了鏈式調用薪者,但挂滓,還沒滿足于此,因為邏輯還沒有更加清晰啸胧。 為了叫買飛機的流程赶站,和開飛機的流程更加清晰。實現(xiàn)了maker纺念、
實現(xiàn)Maker
為了叫買飛機贝椿,我給NSObject實現(xiàn)了分類+Extra.h
+ (CGFloat)makeFlight:(void (^)(MAFlight *))block {
//購買飛機
MAFlight *maker = [[MAFlight alloc]init];
//操作飛機
block(maker);
return maker.distence;
}
再為飛機加一點語法糖
- (MAFlight *)and
{
return self;
}
所以, 就可以這么玩飛機了
[objc makeFlight:^(MAFlight *flight){
flight.takeOff(10.f).and.forward(100000.f);
flight.left(10.f).and.right(1.f);
flight.back(1.f).and.back(1.f).landDown(10.f)
}];
OK, That's all.
下一篇 - iOS Nightmare系列之 - 客戶端應用的Daily Build 與 持續(xù)化集成(Continuous Integration)