美文网首页
链式编程

链式编程

作者: mkb2 | 来源:发表于2018-08-09 15:28 被阅读74次

    说在前面

    项目中用了一个第三方,其中它使用了链式编程。为了封装它,我照葫芦画瓢,写了几个分类方法。但是其中有点技术点不是很清楚,在这里简单记录下。

    本篇博客讨论以下几点:

    • 1.原则:链式编程返回值是一个BlockBlock的返回值是类的实例对象
    • 2.maker.sub(123).add(25)的调用过程是怎样的
    • 3.为什么单独写maker.sub(123)不会警告,而直接写maker.count会警告
    • 4.更优的链式编程写法
    • 5.链式编程实际应用

    1.原则:链式编程返回值是一个Block,Block的返回值是类的实例对象

    先来个加减乘除的Demo

    //.h文件
    #import <Foundation/Foundation.h>
    
    @interface CPMaker : NSObject
    
    @property (nonatomic, assign) double result;
    
    - (CPMaker *(^)(double))add;
    - (CPMaker *(^)(double))sub;
    - (CPMaker *(^)(double))multi;
    - (CPMaker *(^)(double))divide;
    
    - (CPMaker *)test;
    - (NSInteger)count;
    
    @end
    
    //.m文件
    #import "CPMaker.h"
    
    @implementation CPMaker
    
    - (CPMaker *(^)(double))add
    {
        return ^(double num) {
            self.result += num;
            return self;
        };
    }
    
    - (CPMaker *(^)(double))sub
    {
        return ^(double num) {
            self.result -= num;
            return self;
        };
    }
    
    - (CPMaker *(^)(double))multi
    {
        return ^(double num) {
            self.result *= num;
            return self;
        };
    }
    
    - (CPMaker *(^)(double))divide
    {
        return ^(double num) {
            if (num == 0.0) {
                NSAssert(num != 0.0, @"divide num should not equal zero");
                return self;
            }
            self.result /= num;
            return self;
        };
    }
    
    - (CPMaker *)test
    {
        NSLog(@"%s",__func__);
        return self;
    }
    
    - (NSInteger)count
    {
        return 5;
    }
    
    @end
    
    //实际调用过程
    void maker()
    {
        CPMaker *maker = [CPMaker new];
        double result = maker.add(5).sub(42).multi(12).divide(23).result;
        maker.sub(12);
        NSLog(@"result = %f",result);
    }
    

    CPMaker要通过链式编程,调用的对象函数返回值必须是Block,同时该Block的返回值必须是CPMaker的实例对象,也就是self


    2.maker.sub(123).add(25)的调用过程是怎样的

    链接地址

    为之精髓.jpg

    如上所示

    maker.sub(123)  //一共执行了两步操作
    可以理解为:
    
    CPMaker *(^sub)(double) = [make sub];//1.获取Block
    subBlock(23);//2.执行有参数的Block
    
    所以我们直接写maker.sub(123)是不会报错或警告的。
    maker.sub(123).add(25) 也就不难理解了。
    

    为什么返回值是Block?
    唯有返回值是Block,在调用的时候才会有maker.add(5).sub(42)这样的写法。
    即可以带参数。
    如果返回值不是Block,类似于test方法,写法就是(void)maker.test.test.test;样式,看着都别扭。如果不加(void)会警告,显示get方法没有使用起返回值,虽然警告但是可以正常执行。

    为什么Block返回值是CPMaker的实例对象?
    当执行Block,返回对象本身,才可以接着调用其他的链式方法,即拼接更多的方法。
    如:maker.add(5).sub(42).multi(12).divide(23)


    3.为什么单独写maker.sub(123)不会警告,而直接写maker.count会警告

    maker.sub(123)写法是正确的,两步拼成了一句话,看着有点难理解而已。
    maker.count表示的是get方法,我们没有用他的返回值,所以警告,但是get方法会正常调用。


    4.更优的链式编程写法

    以maker.add(23)为例,如上写法有什么问题?

    点式调用没有参数类型提示,API设计不够友好
    maker.add    
    

    什么是更好的设计?

    //.h文件对象方法全部替换成readOnly属性(其实还是get方法)
    @property (nonatomic, readonly) CPMaker *(^add)(double);
    @property (nonatomic, readonly) CPMaker *(^sub)(double);
    @property (nonatomic, readonly) CPMaker *(^multi)(double);
    @property (nonatomic, readonly) CPMaker *(^divide)(double);
    
    //实际调用效果有参数类型的提示
    maker.add(double)
    

    5.链式编程实际应用

    实际链式编程中,如下写法非常失败.
    CPMaker不该手动创建并且暴露在外

    void maker()
    {
        CPMaker *maker = [CPMaker new];
        double result = maker.add(5).sub(42).multi(12).divide(23).result;
        NSLog(@"result = %f",result);
    }
    

    实际应用中可能会有以下几种情况:

    1.新建容器类,提供类方法,提供链式编程能力

    //.h文件
    #import <Foundation/Foundation.h>
    #import "CPMaker.h"
    
    @interface CPCalculator : NSObject
    
    //假如有参数,就会有提示,Xcode新的特性
    @property (nonatomic, class, readonly) CPMaker *(^calculator)(void);
    
    @end
    
    //.m文件
    #import "CPCalculator.h"
    
    @implementation CPCalculator
    
    + (CPMaker *(^)(void))calculator
    {
        return ^(void){
            return [CPMaker new];
        };
    }
    
    @end
    
    //.实际调用
    CPCalculator.calculator().add(4).sub(5).multi(5).divide(23);
    

    2.为分类提供方法,插入Block。例如Masonry

    仿写Masonry

    //1.新建分类方法
    #import <UIKit/UIKit.h>
    #import "CPDConstraintMaker.h"
    
    @interface UIView (CPDMasonry)
    
    - (void)cpd_updateConstraints:(void(^)(CPDConstraintMaker *))maker;
    
    @end
    
    //2.CPDConstraintMaker内部的参数
    #import <Foundation/Foundation.h>
    #import "CPDConstraint.h"
    
    @interface CPDConstraintMaker : NSObject
    
    @property (nonatomic, readonly) CPDConstraint *left;
    @property (nonatomic, readonly) CPDConstraint *right;
    @property (nonatomic, readonly) CPDConstraint *top;
    @property (nonatomic, readonly) CPDConstraint *bottom;
    @property (nonatomic, readonly) CPDConstraint *width;
    @property (nonatomic, readonly) CPDConstraint *height;
    
    @end
    
    //3.CPDConstraint内部比较方法
    @interface CPDConstraint : NSObject
    
    @property (nonatomic, readonly) CPDConstraint *(^cpd_equalTo)(CGFloat num);
    
    @end
    
    //4.实际调用
        [self.view cpd_updateConstraints:^(CPDConstraintMaker *maker) {
            maker.left.cpd_equalTo(12);
            maker.height.cpd_equalTo(4);
        }];
    

    相关文章

      网友评论

          本文标题:链式编程

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