订单在提交的时分会面对不同的校验规矩,不同的校验规矩会有不同的处理。假定这个处理便是弹窗。

有的时分会射中规矩1,则弹窗1,有的时分同时射中规矩1、2、3,但由于存在规矩的优先级,则会处理优先级最高的弹窗1。

老的事务背景下,弹窗优先级或者说校验规矩是统一的。直接用函数翻译完成,写多个 if 问题不大。

但在新事务背景下,不同的条件,弹窗优先级不一致,之前的写法需求写很多的嵌套判别,代码难以保护。

所以问题抽象为:怎么规划一个校验器

问题背景

为了清晰说明问题,假定线上的弹窗校验规矩为:A -> B -> C

typedef NS_ENUM(NSUInteger, OrderSubmitReminderType) {
    OrderSubmitReminderTypeNormal = 0,    // 没有射中校验规矩
    OrderSubmitReminderTypeA,             // 射中校验规矩 A
    OrderSubmitReminderTypeB,             // 射中校验规矩 B
    OrderSubmitReminderTypeC,             // 射中校验规矩 C
}

老规矩比较简单,不存在不同的校验规矩,所以需求能够直接用代码翻译,不需求额外规划

+ (OrderSubmitReminderType)acquireOrderValidateType:(id)params {
    if ([OrderSubmitUtils validateA:params]) {
        return OrderSubmitReminderTypeA;
    }
    if ([OrderSubmitUtils validateB:params]) {
        return OrderSubmitReminderTypeB;
    }
    if ([OrderSubmitUtils validateC:params]) {
        return OrderSubmitReminderTypeC;
    }
    return OrderSubmitReminderTypeNormal;
}

假定只要2个弹窗条件:是否是 VIP 账户(isVIP)、是否是付费用户(isChargedAccount)。

  • isVIP & isChargedAccount: A -> B -> C
  • isVIP & !isChargedAccount:B -> C-> A
  • !isVIP: C -> B -> A

假如直接改,代码便是一坨废物了

+ (OrderSubmitReminderType)acquireOrderValidateType:(id)params {
    if (isVIP) {
       if (isChargedAccount) {
            if ([OrderSubmitUtils validateA:params]) {
                return OrderSubmitReminderTypeA;
            }
            if ([OrderSubmitUtils validateB:params]) {
                return OrderSubmitReminderTypeB;
            }
            if ([OrderSubmitUtils validateC:params]) {
                return OrderSubmitReminderTypeC;
            }
            return OrderSubmitReminderTypeNormal;
       } else {
            if ([OrderSubmitUtils validateB:params]) {
                return OrderSubmitReminderTypeB;
            }
            if ([OrderSubmitUtils validateC:params]) {
                return OrderSubmitReminderTypeC;
            }
            if ([OrderSubmitUtils validateA:params]) {
                return OrderSubmitReminderTypeA;
            }
            return OrderSubmitReminderTypeNormal;
       } 
    } else {
            if ([OrderSubmitUtils validateC:params]) {
                return OrderSubmitReminderTypeC;
            }
             if ([OrderSubmitUtils validateB:params]) {
                return OrderSubmitReminderTypeB;
            }
            if ([OrderSubmitUtils validateA:params]) {
                return OrderSubmitReminderTypeA;
            }
            return OrderSubmitReminderTypeNormal;
    }
}

思路

可能有些人会觉得,那不简单,我将不同组合条件下的弹窗抽取为3个办法,照样很简洁

+ (OrderSubmitReminderType)acquireOrderValidateTypeWhenIsVIPAndChargedAccount:(id)params {
    // A->B->C
    if ([OrderSubmitUtils validateA:params]) {
        return OrderSubmitReminderTypeA;
    }
    if ([OrderSubmitUtils validateB:params]) {
        return OrderSubmitReminderTypeB;
    }
    if ([OrderSubmitUtils validateC:params]) {
        return OrderSubmitReminderTypeC;
    }
    return OrderSubmitReminderTypeNormal;
}
+ (OrderSubmitReminderType)acquireOrderValidateTypeWhenIsVIPAndNotChargedAccount:(id)params {
   // B -> C-> A
   if ([OrderSubmitUtils validateB:params]) {
        return OrderSubmitReminderTypeB;
    }
    if ([OrderSubmitUtils validateC:params]) {
        return OrderSubmitReminderTypeC;
    }
    if ([OrderSubmitUtils validateA:params]) {
        return OrderSubmitReminderTypeA;
    }
    return OrderSubmitReminderTypeNormal;
}
+ (OrderSubmitReminderType)acquireOrderValidateTypeWhenIsNotVIP:(id)params {
   // C -> B-> A
   if ([OrderSubmitUtils validateC:params]) {
        return OrderSubmitReminderTypeC;
    }
     if ([OrderSubmitUtils validateB:params]) {
        return OrderSubmitReminderTypeB;
    }
    if ([OrderSubmitUtils validateA:params]) {
        return OrderSubmitReminderTypeA;
    }
    return OrderSubmitReminderTypeNormal;
}

其实不然,问题仍是很多:

  • 虽然抽取为不同办法,可是每个办法内部存在很多冗余代码,因为每个校验规矩的代码是相同的,重复存在,只不过先后顺序不同
  • 存在隐含逻辑。 return 顺序决定了弹窗优先级的凹凸(这一点不行痛)

计划

那能不能优化呢?有3个思路:责任链规划形式、工厂规划形式、战略形式

战略形式:当需求依据客户端的条件挑选算法、战略时,可用该形式,客户端会依据条件挑选合适的算法或战略,并将其传递给运用它的对象。典型规划前端 Vue-Validator form 各种 rules

责任链形式:当需求依据恳求的内容挑选处理器时,可用该形式,恳求会沿着链传递,直到被处理,如 Node 洋葱模型

不过现在来看,战略形式被 Pass 了

责任链规划形式

采用责任链规划形式。基类 OrderSubmitBaseValidator 声明接口,是一个抽象类

  • 有一个特点 nextValidator 用于指向下一个校验器
  • 有一个办法 - (void)validate:(id)params; 用于处理校验,内部默认完成是传递给下一个校验器。
//.h
OrderSubmitBaseValidator {
    @property nextValidator;
    - (void)validate:(id)params;
    - (BOOL)isValidate:(id)params;
    - (void)handleWhenCapture;
}
// .m
#pragma mark - public Method
- (BOOL)isValidate:(id)params {
    Assert(0, @"must override by subclass");
    return NO;
}
- (void)validate:(id)params {
    BOOL isValid = [self isValidate:params];
    if (isValid) {
        [self.nextValidator validate:params];
    } else {
       [self handleWhenCapture];
    }
}
- (void)handleWhenCapture {
    Assert(0, @"must override by subclass");
}

然后针对不同的校验规矩声明不同的子类,继承自 OrderSubmitBaseValidator。依据A、B、C 3个校验规矩,有:OrderSubmitAValidator、OrderSubmitBValidator、OrderSubmitCValidator。

子类去重写父类办法

OrderSubmitAValidator {
    - (BOOL)isValidate:(id)params  {
        // 处理是否满足校验规矩A
    }
    - (void)handleWhenCapture {
        // 当不满足条件规矩的时分的处理逻辑
        displayDialogA();
    }
}

为了规划的强健,假定没有射中任何校验规矩,需求怎么处理?这个才能需求有兜底默认的行为,比如打印日志:NSLog(@"暂无射中任何弹窗类型,参数为:%@",params); 也能够由事务方传递

OrderSubmitDefaultValidator *defaultValidator = [OrderSubmitDefaultValidator validateWithBloock:^ {
    SafeBlock(self.deafaultHandler, params);
    if (!self.deafaultHandler) {
        NSLog(@"暂无射中任何弹窗类型,参数为:%@",params);
    }
}];

初始化多个校验规矩

OrderSubmitAValidator *aValidator = [[OrderSubmitAValidator alloc] initWithParams:params];
OrderSubmitBValidator *bValidator = [[OrderSubmitBValidator alloc] initWithParams:params];
OrderSubmitCValidator *cValidator = [[OrderSubmitCValidator alloc] initWithParams:params];

不同优先级的校验怎么指定:

if (isVIP) {
    if (isChargedAccount) {
        aValidator.nextValidator = bValidator;
        bValidator.nextValidator = cValidator;
    } else {
        bValidator.nextValidator = cValidator;
        cValidator.nextValidator = aValidator;
    }
} else {
    cValidator.nextValidator = bValidator;
    bValidator.nextValidator = aValidator;
}

但仍是不行高雅,这个优先级需求用户感知。能不能做到事务方只传递参数,内部判别射中什么弹窗优先级组合。所以接口能够规划为

[OrderSubmitValidator validateWithParams:params handleWhenNotCapture:^{
    NSLog(@"暂无射中任何弹窗类型,参数为:%@",params);
}];

上述办法其实等价于

let validateType = [OrderSubmitValidator generateTypeWithParams:params];
[OrderSubmitValidator validateWith:validateType];

validateWith 办法内部依据 validateType 去拼装 Map 的 key,然后从 Map 中取出详细规矩组合,然后依次迭代遍历履行

let rulesMap = {
	isVIP && isCharged : [a-b-c-d],
	isVIP && !isCharged: [a-b-d-c],
  !isVIP: [a-c-d-b],
}

长处:

  1. 处理了现在的错误弹窗的隐含逻辑,后续人接手,弹窗优先级清晰可见,进步可保护性,减少犯错概率
  2. 关于判别(校验)的增减都无需关怀其他的校验规矩。相似保护链表,仅在一开始指定即可,符合“开闭原则
  3. 关于现有校验规矩的修改满足收口,每个规矩都有自己的 validator 和 validate 办法
  4. 现在弹窗优先级针对 EVA 、BTC 存在不同优先级顺序,假如依照现有的计划实施,则会存在很多冗余代码

工厂规划形式

规划基类

OrderSubmitBaseValidator {
    - (void)validate;
    - (BOOL)validateA;
    - (BOOL)validateB;
    - (BOOL)validateC;
}
- (void)validate {
    Assert(0, @"must override by subclass");
}
- (BOOL)validateA {
    // 判别是否射中规矩 A
}
- (BOOL)validateB {
    // 判别是否射中规矩 B
}
- (BOOL)validateC {
    // 判别是否射中规矩 C
}

依据不同的弹窗优先级条件,声明3个不同的子类:OrderSubmitAValidatorOrderSubmitBValidatorOrderSubmitCValidator。各自重写 validate 办法

OrderSubmitAValidator {
    - (void)validate {
        [self validateA];
        [self validateB];
        [self validateC];
    }
}
OrderSubmitBValidator {
    - (void)validate {
        [self validateB];
        [self validateC];
        [self validateA];
    }
}
OrderSubmitCValidator {
    - (void)validate {
        [self validateC];
        [self validateB];
        [self validateA];
    }
}

规划工厂类OrderSumitValidatorFactory,提供工厂初始化办法

OrderSumitValidatorFactory {
    + (OrderSubmitBaseValidator *)generateValidatorWithParams:(id)params;
}
+ (OrderSubmitBaseValidator *)generateValidatorWithParams:(id)params {
    if (isVIP) {
        if (isChargedAccount) {
            return [[OrderSubmitAValidator alloc] initWithParams:params];
        } else {
            return [[OrderSubmitBValidator alloc] initWithParams:params];
        }
    } else {
           return [[OrderSubmitCValidator alloc] initWithParams:params];
    }
}

长处:

  • 没有重复逻辑,判别办法都守口在基类中
  • 优先级的关系保护在不同的子类中,各司其职,独立保护

最后选什么?组合优于继承,个人倾向运用责任链形式去组织。