Objective-C 实现链式调用的 DSL

1,013 阅读3分钟
原文链接: blog.loujiwei.cn

背景

DSL(Domain Specific Language),用于解决特定领域的语言. 链式调用就是类似RxJava那样使用连续的.方法来调用, 这样可使得调用逻辑清晰, 减少代码量.

比如Masonry的语法就是:

// Masonry
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.equalTo(superview.mas_top).with.offset(padding.top);
    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);
}];

问题

现在拿实际项目中的例子说明

页面需要显示订单头行+订单统计行+普通订单行.

基本思路:

  1. 我们最终需要得到list_all<OrderItemModel>的列表
  2. 先往list_all中添加4行头item.
  3. 将通过服务器api获取的产品列表list_a元素转成统一对象OrderItemModel生成list_b<OrderItemModel>
  4. list_b排序(先本品, 再赠品)
  5. 在产品列表list_b中添加订单统计头行
  6. list_b append到list_all后面形成最终展示数据源

期望的调用方式:

resultList = LoadPreList(list_a)  // 3.接受网络获取的数据列表list_a
            .convertToOrderItemModel(rulesBlock)  // 3.将list_a转成list_a, 需外部自己实现
            .sortAndAddTotalHeader()    // 4. 5.将list按先本品, 后赠品的顺序排序, 再添加统计头信息;其实这一步对外可以省略, 因为无需传参
            .appendTo(list_all);    // 6.将完成后的list追加到只包含订单头行的list_all后面

实现

1. 初始化OrderUtil

LoadPreList接收一个NSMutableArray类型的参数, 并直接返回

OrderListUtils.h


#import 
@class OrderListUtils;
OrderListUtils *LoadPreList(NSMutableArray *list_a);
@interface OrderListUtils : NSObject
@property NSMutableArray *preList;
@end

OrderListUtils.m

@implementation OrderListUtils
OrderListUtils *LoadPreList(NSMutableArray *list_a) {
    OrderListUtils *orderListUtils = [[OrderListUtils alloc] init];
    orderListUtils.preList = list_a;
    return orderListUtils;
}
@end

使用c的方式定义方法, 可以直接用LoadPreList(list_a)的方式调用.

2. 实现任意对象转换到OrderItemModel

只要让外部实现id --> OrderItemModel即可.
要实现.调用方法, 并且使用()传参, 需使用block的方式实现.

LoadPreList(list_a)返回一个OrderListUtils对象, 所以我们在OrderListUtils里面定义一个名为convertToOrderItemModel的方法,这个方法返回一个ConvertOrderItemModel的block

OrderListUtils.h

typedef id(^ruleBlock)(id item);
typedef OrderListUtils *(^ConvertOrderItemModelBlock)(ruleBlock block);
@interface OrderListUtils : NSObject
// 将数据list里的item转成OrderItemModel的格式
- (ConvertOrderItemModelBlock)convertToOrderItemModel;
@end

OrderListUtils.m

// 将数据list里的item转成OrderItemModel的格式
- (ConvertOrderItemModelBlock)convertToOrderItemModel {
    ConvertOrderItemModelBlock convertOrderItemModel = ^OrderListUtils *(ruleBlock block) {
        NSMutableArray *items = @[].mutableCopy;
        for (id item in self.preList) {
            [items addObject:block(item)];
        }
        self.preList = items; //将preList指向转换好的列表
        
        return self;
    };
    return convertOrderItemModel;
}

我们看这里的方法申明:

- (ConvertOrderItemModelBlock)convertToOrderItemModel;

这里方法名convertToOrderItemModel 等价于 返回值blockConvertOrderItemModelBlock. 我们可以称返回值blockConvertOrderItemModelBlock代替block

方法实现里面套用外部传入的blockruleBlockself.preList的每个元素都转换成OrderItemModel的类型

3. 对OrderItemModel类型的列表进行排序 并 添加统计行

由于这一步外部无需传入参数, 并且这一步是整个流程的必备步骤, 所以可以将这一步的操作放入步骤4里, 从而简化调用代码.

4. 组装最终list

本步骤接受一个只包含4个订单头信息的列表, 并将上一步所得的列表append到这个列表后面

定义出这个代替block:

typedef NSMutableArray *(^appendToBlock)(NSMutableArray *headerList);
- (appendToBlock)appendTo;

实现

- (appendToBlock)appendTo {
    appendToBlock block = ^NSMutableArray *(NSMutableArray *headerList) {
        int beginIndex = [headerList count] - 1;
        return [self convertToOrderMainList:headerList headerListOrderItemBeginIndex:beginIndex preList:self.preList];
    };
    return block;
}

完整代码

OrderItemUtils.h

@class OrderListUtils;
@class OrderItemModel;
typedef id(^ruleBlock)(id item);
typedef OrderListUtils *(^ConvertOrderItemModelBlock)(ruleBlock block);
typedef NSMutableArray *(^appendToBlock)(NSMutableArray *headerList);
OrderListUtils *LoadPreList(NSMutableArray *list_a);
@interface OrderListUtils : NSObject
@property NSMutableArray *preList;
// 将数据list里的item转成OrderItemModel的格式
- (ConvertOrderItemModelBlock)convertToOrderItemModel;
// 将网络列表 排序, 添加统计行, 并添加到只包含订单头信息的列表中
- (appendToBlock)appendTo;
@end

OrderItemUtils.m

@implementation OrderListUtils
OrderListUtils *LoadPreList(NSMutableArray *list_a) {
    OrderListUtils *orderListUtils = [[OrderListUtils alloc] init];
    orderListUtils.preList = list_a;
    return orderListUtils;
}
// 将数据list里的item转成OrderItemModel的格式
- (ConvertOrderItemModelBlock)convertToOrderItemModel {
    ConvertOrderItemModelBlock convertOrderItemModel = ^OrderListUtils *(ruleBlock block) {
        NSMutableArray *items = @[].mutableCopy;
        for (id item in self.preList) {
            [items addObject:block(item)];
        }
        self.preList = items; //将preList指向转换好的列表
        return self;
    };
    return convertOrderItemModel;
}
- (appendToBlock)appendTo {
    appendToBlock block = ^NSMutableArray *(NSMutableArray *headerList) {
        int beginIndex = [headerList count] - 1;
        return [self convertToOrderMainList:headerList headerListOrderItemBeginIndex:beginIndex preList:self.preList];
    };
    return block;
}
// 将OrderItemModel格式的list排序 并添加统计头
// beginIndex 为订单行项目开始的index
- (NSMutableArray *)convertToOrderMainList:(NSMutableArray *)headerList headerListOrderItemBeginIndex:(int)beginIndex preList:(NSMutableArray *)preList {
    // pass
}
@end