2

PrioritySessionElement 设计与使用

 3 years ago
source link: https://www.yuque.com/runscode/ios-thinking/priority_element
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
Join Yuque to read better

Want to follow this author to read more or favorite this article? Please or to Yuque

PrioritySessionElement 设计与使用

●用来解决什么问题(背景)
●API功能概览
●功能设计介绍
●Example
●公司工程如何引用
用来解决什么问题
●一开始是用来解决单车活动优先级问题,简化逻辑代码,做到高可读,可扩展。
●后续慢慢在使用过程中逐渐衍生新的功能(延时,轮询,条件校验等)
●当然最大的特点就是把回调地狱展平把核心都放在同一层级
不知道说什么,以代码图示例
为改进之前 代码是这样写的
SwiftCopy Code
func function0() {
    obj0.closure { _ in
        // to do something
        obj1.closure { _ in
            // to do something                      
            obj2.closure { _ in
                ...
                objn.closure { _ in
                       ...
                }         
            }             
        }        
    }
}

or

func function1 {
    
    if condition0 {
        // to do something
    } else if condition1 {
        // to do something
    } else if condition2 {
        // to do something
    }
    ...
    else {
        // to do something
    }
}

and other condition statement
分析上面那种代码我们可以得出几点结论:
●上面的代码不管怎么看都是按流程来的或者不同的条件走不同的条件流程
●可读性与可维护性一般还行,二次修改错误率很高
●扩展性一般几乎没有,只会无限增加代码的行数、条件分支以及回调更深层级
●如果功能升级增加类似延迟、轮询,那完全不支持。
●复用性可以说无
所以就针对问题解决问题:
●实现一个容器(Element)搭载所有外部实现逻辑
●容器(Element)以单向链表的方式链接,执行完就自动执行下一个
●容器内聚合一个抽象条件逻辑助手(Promise),可随意扩展增加行为,用来检查外部实现是否可以执行链表下一个Element(可以形象理解为自来水管路的阀门,电路电气开关之类,当然会有更复杂的阀门与电气开关)
●自己管理自己的生命周期,无需外部强引用
●容器(Element)可以被继承实现参考NSOperation设计
图示

API功能预览
Element遵守的协议头文件
Objective-CCopy Code
@protocol PriorityElementProtocol <NSObject>

@required
- (void)nextWithValue:(id _Nullable)value;
- (void)breakProcess;

- (void)onSubscribe:(id _Nullable)data;
- (void)onCatch:(NSError *_Nullable)error;

///  can not override by subclass
- (void)execute;
- (void)executeWithData:(id _Nullable)data;

/// override by subclass
- (void)executePromise:(PriorityPromise *)promise;

@end
Element头文件
Objective-CCopy Code
/// The correct way to use
#define PromisePriority(T,V) PrioritySessionElement<T, V> elementWithPromiseBlock:^(PriorityPromise<T, V> *promise)

/// Input: Input parameter type Output: Data type for the next element
@interface PrioritySessionElement<Input, Output> : NSObject<PriorityElementProtocol>

typedef void(^ExecutePromiseBlock)(PriorityPromise<Input, Output> *promise);
typedef PrioritySessionElement *_Nonnull(^Then)(PrioritySessionElement *session);

@property (nonatomic, weak) Input input;

/// Element id
@property (nonatomic, copy) NSString *identifier;
/// link to the next Element
@property (nonatomic, strong, readonly) Then then;

@property (nonatomic, strong, readonly) PriorityPromise *promise;

/// It is recommended to use macros$It is recommended to use macros `PromisePriority`
+ (instancetype)elementWithPromiseBlock:(ExecutePromiseBlock)block;
+ (instancetype)elementWithPromiseBlock:(ExecutePromiseBlock)block identifier:(NSString *_Nullable)identifier;

/// Subscribe to the normal process and successfully process the result callback
- (instancetype)subscribe:(void (^)(Output _Nullable value))subscribe;
/// Process interrupt callback
- (instancetype)catch:(void (^)(NSError *_Nullable error))catch;
/// The process ends with a callback regardless of success
- (instancetype)dispose:(dispatch_block_t)dispose;
/// Manual interrupt link list and release self-promise
- (void)breakPromiseLoop;

@end
Promise 头文件
Objective-CCopy Code
@protocol PriorityElementProtocol;

@interface PriorityPromise<Input, Output> : NSObject

typedef void(^PriorityPromiseNext)(Output _Nullable value);
typedef void(^PriorityPromiseBreak)(NSError *_Nullable err);
typedef void(^PriorityPromiseValidated)(BOOL isValid);
typedef void(^PriorityPromiseLoopValidated)(BOOL isValid, NSTimeInterval interval);
typedef void(^PriorityPromiseConditionDelay)(BOOL condition, NSTimeInterval interval);

@property (nonatomic, copy) NSString *identifier;

@property (nonatomic, strong) id<PriorityElementProtocol> element;

/// Pipeline data delivery
@property (nonatomic, strong) Input input;
@property (nonatomic, strong) Output output;

/// execute  next element
@property (nonatomic, strong, readonly) PriorityPromiseNext next;

/// Check execution Check by executing the next or interrupting the current process
/// validated(BOOL isValid)
///
/// @parameter isValid Judge conditions for next
/// Similar to if(isValid) next()
/// isValid == false -> will call brake error and call catch block
@property (nonatomic, strong, readonly) PriorityPromiseValidated validated;

/// Cycle check until the check pass (isValid is true) to perform the next interval check interval default 0
/// loopValidated(BOOL isValid, NSTimeInterval interval)
///
/// @parameter isValid Judge conditions for polling
/// if isValid is false,start loop self,
/// if isValid is true, end loop and execute next element
///
/// @parameter interval if interval < 0, will call brake error and call catch block

@property (nonatomic, strong, readonly) PriorityPromiseLoopValidated loopValidated;

/// Check whether the condition needs to delay the next step
///
/// @parameter condition
/// @parameter interval time interval
///
/// (condition == true && interval > 0) -> delay interval execute next element
/// (condition == false || interval <= 0) -> execute next element
///
@property (nonatomic, strong, readonly) PriorityPromiseConditionDelay conditionDelay;

/// Break the current process, release all objects after the list
@property (nonatomic, strong, readonly) PriorityPromiseBreak brake;

+ (instancetype)promiseWithInput:(Input)data element:(id<PriorityElementProtocol>)ele;
+ (instancetype)promiseWithInput:(Input)data element:(id<PriorityElementProtocol>)ele identifier:(NSString *)identidier;
- (void)breakLoop;
功能设计介绍Element
●采用OC泛型设计,具有泛型限定(编译期警告),以及泛型语法提示,提高编码效率以及类型检查
●SessionElement是个单向链表,通过then语法绑定下一个Element
●每个SessionElement聚合一个Promise,只作为容器使用不参与任何额外逻辑补充
●SessionElement提供对外的消息订阅(subcribe)以及异常捕获(catch)和释放(dispose)通知
●SessionElement提供两种构造方式(init and macro),个人建议使用宏macro,避免过长的语法影响阅读

Promise
●采用OC泛型设计,具有泛型限定(编译期警告),以及泛型语法提示,提高编码效率以及类型检查
●next:执行下一个Element
●validated:校验外部给定条件执行
●loopValidated:轮询校验外部给定条件执行
●conditionDelay:根据外部条件结果决定是否延迟执行下一个Element
●brake:打断所有流程and执行结束
●breakLoop:强制结束轮询
●可以看出Promise是Element相互强引用造成一个循环引用闭环,结束的时候打破闭环达到内存释放的结果,这么做其实故意的,为了更方便使用更加简洁避免其他外部引用的干扰。
●Input:上一个Element传给当前Element的参数
●Outout:当前Element传递给下一个Element的参数
●Input和Output这种内部传递设计是为了保护结果变量在一处使用不会被到处使用修改逻辑内聚
详细源码请参考,欢迎大家指正:https://github.com/RunsCode/PromisePriorityChain也许大家看到这里问到一个一股熟悉的Goolge开源的Promises/mxcl的PromiseKit或者RAC等的味道
那么为啥不用那些个神的框架来解决实际问题呢?
主要有一点:框架功能过于丰富实现相对复杂,而我呢,弱水三千我只要一瓢,越轻越好的原则!哈哈
Example ( Talk is cheap,show me code 😄 )常见if else 嵌套
常规写法
SwiftCopy Code
func doSomethings() {

    if (满足活动0条件) {
        // 执行活动0 并结束
    } else if (满足活动1条件) {
        // 执行活动1 并结束
    }  else if (满足活动2条件) {
        // 执行活动2 并结束
    }
    ...
    else if (满足活动 n 条件) {
        // 执行活动n 并结束
    } else {
        // default to do
    }
}
改进之后的写法
Objective-CCopy Code
// 单纯使用next和brake 的方式
- (PrioritySessionElement *)huoDong_0_Element {
    return [[[[PromisePriority(NSString *, NSNumber *){
        //一系列操作检查是否满足活动条件
        ... ...
        // 满足活动条件话,打断当前流程,不用执行下一个活动
        promise.brake(nil);
        
        // 不满足活动条件话,继续下一个流程
      promise.next(nil);
        
    } identifier:@"huoDong_0_Element"] subscribe:^(NSNumber * _Nullable value) {
        
        NSLog(@"huoDong_0_Element subscribe data = %@", value);
        
    }] catch:^(NSError * _Nullable error) {
        
        // 满足活动0 打断其他所有链路 并释放链表
        
    }] dispose:^{ 
        // 释放通知
  }];

// 使用isValid 的方式
- (PrioritySessionElement *)huoDong_1_Element {
    return [PromisePriority(NSNumber *, NSNumber *){
        // YES 表示当前活动不满足需要执行下一个活动
        // NO 表示当前活动满足, 打断流程 释放资源
        BOOL isValidCouldToNext = YES; 
        //一系列骚操作检查是否满足活动条件
        ...
            isValidCouldToNext = ...;
        ...
        
        id data = promise.input;上一个活动传递来的参数
        promise.output = @10086;//传递给下一个活动的参数
        promise.validated(isValidCouldToNext);

    } identifier:@"huoDong_1_Element"];
}
    
- (PrioritySessionElement *)huoDong_2_Element {
    return [PromisePriority(NSNumber *, NSNumber *){...}];  
    
- (PrioritySessionElement *)huoDong_3_Element {
    return [PromisePriority(NSNumber *, NSNumber *){...}];  
    
- (PrioritySessionElement *)huoDong_n_Element {
    return [PromisePriority(NSNumber *, NSNumber *){...}];
Objective-CCopy Code
PrioritySessionElement<NSString *, NSNumber *> *headElement = [self huoDong_0_Element];
headElemnt.then(self.huoDong_1_Element)
    .then(self.huoDong_2_Element)
    .then(self.huoDong_3_Element)
    ... // and so on
    .then(self.huoDong_n_Element);

// 执行活动检查
headElement.execute();
篇幅有限,其他用法参考:
Objective-CSwiftJava & Android

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK