美文网首页iOS开发系列超棒文集链式编程
可能被忽略掉编程技巧 之 链式编程

可能被忽略掉编程技巧 之 链式编程

作者: JARVIS_STUDIO | 来源:发表于2016-03-21 16:08 被阅读700次

    可能被忽略掉编程技巧 之 链式编程


    @author Jou Email Weibo or Github


    可能被忽略掉编程技巧 之 链式编程

    预热 - 链式编程

    哎呦,不错哦。有点时间, 随便写篇文章,聊聊iOS中的链式编程。
    链式编程? 如果你不是iOS开发者,相信你应该没听过链式编程。对的, 链式编程,并不是一种编程范式。链式编程这种表述, 应该是在Objective-C独有,因为Objective-C是一门繁琐而奇怪的语言。
    在Swift之前,想成为iOS开发者,在入门前你就有了两个门槛: 1. 你要有台苹果, 2. 你要肯于接受Objective-C的反人类,和繁琐。
    所以形式上,链式编程是"点方法"的调用,来弥补Objc的繁琐。

    Objective-C

    
    NSString objc = @"Hello world";
    [objc lowercaseString]; // 给objc 发送lowercaseString的消息
    
    

    而相比之下swift 就更为简洁

    let str = "Hello, playground"
    str.lowercaseString;
    
    

    链式编程 - 点式调用 LinkBlock

    为了实现Objc中的点式调用, Novo已经有了一个基于block的好玩的实现LinkBlock
    LinkBlock 旨在帮助我们实现点方法调用的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];
    
    
    
    //如果使用链式编程的方式,大部分工作可以在思路连续的情况下进行
    //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写法,在代码实现上也过于繁琐。
    所以很大程度上, 一部分人转而重用storyboard界面布局,一部分转而重度依赖mansory, 还有大多数在结合使用。
    而Masonry的实现上, 恰恰就是围绕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实现、

    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);
    }];
    
    

    显而易见的、真是简洁到没朋友。

    ps: Masonry 在github上 已有1W+的star。


    除了形式上的链式调用, Objc中所谈的链式调用, 更是函数编程中一种常用的编程手段。

    函数式编程 - Function Chaining

    在swift中引入了函数式编程之后,把函数编程推到了新的高度。
    而在swift中, 偶尔会见到这样的代码。

    
    let result = students |> filterOutFailed |> sortByGrade |> formatForPrint |> joinByComma
    
    
    

    把 ‘|>’ 换成 ’.' ,便有了极大的相似并不是偶然, 链式编程,只是函数编程思想的一种结合。

    百分百的知识 - 实践 链式编程

    如果团队有人在项目中,没有讨论的贸然加入了LinkBlock框架, 我肯定还是会argue一下的。
    因过于依赖于LinkBlock,在代码可读性上增加了难度,并且增加了不可预期的崩溃。
    LinkBlock中不能很好的处理,给nil进行链式调用,造成崩溃。

    所以我认为,链式编程更好的实践,应该是应用在复用性较好的组件中, 最好还可以像masonry一样,使用block包裹起来。

    如, 语句块在objc的使用方式之一:

    self.tableView.tableFooterView = ({
      UIView *view = [[UIView alloc]initWithFrame:CGRectZero];
      view;
    });
    
    

    这不是纯秀技巧, 在代码可读性上, 他的模块, 和逻辑更加清晰。

    所以我最近在考虑,将链式编程的思想加入我自己的 开源项目Router, 或 Dispatcher 中。 而形式上,则更类似Masonry的maker。

    场景是 : 假如,我买了一架灰机, 我要驾驶它。

    使用Block的特性,Trick实现链式调用

    那么,我的飞机,一定可以起飞,降落, 上下左右,依照block的特性。我可以这么实现它

    
    - (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);
    
    

    好了那么我们已经实现了链式调用,但,还没满足于此,因为逻辑还没有更加清晰。 为了叫买飞机的流程,和开飞机的流程更加清晰。实现了maker、

    实现Maker

    为了叫买飞机,我给NSObject实现了分类+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 与 持续化集成(Continuous Integration)

    相关文章

      网友评论

      本文标题:可能被忽略掉编程技巧 之 链式编程

      本文链接:https://www.haomeiwen.com/subject/jpphlttx.html