美文网首页
(WWDC) 高级iOS应用程序架构和模式

(WWDC) 高级iOS应用程序架构和模式

作者: FicowShen | 来源:发表于2019-06-05 11:44 被阅读0次

    内容概览

    • 前言
    • 设计信息流
    • 定义明确的责任
    • 用不变性进行简化




    前言

    大多数大型应用的架构都会经历从简单到复杂的过程。

    随着业务逻辑不断增多,应用需要处理的任务也会越来越繁杂,各种bug也会显现出来。
    如果应用没有采用合适的软件架构和模式,问题将变得更加棘手。

    你对此是否深有体会?

    简单架构 规模逐步扩大的简单架构 不合理的简单架构 不合理的复杂架构 不合理的复杂架构导致的问题开始突显 在不合理的架构上进行单元测试 单元测试成为了新的麻烦




    针对这些问题,我们需要对架构进行不断地探索,因此我们对架构的理解也会更加深入。
    渐渐地,我们对架构的要求会越来越高。然而,往往我们的能力没有和我们的品位一起提升。


    架构不是教条主义,它需要我们的洞察力!




    设计信息流

    混乱的信息流 清晰直观的信息流




    真实数据 和 派生数据



    派生数据的特点:

    • 根据输入数据计算得出
    • 当输入数据变化时,需要重新计算
    • 很像缓存




    示例:

    这是一个很常见的需求。
    在界面放置一个UITextView,旁边有一个UILabel作为UITextView的文字计数器。
    当UITextView的文本发生变化时,我们会在代理方法中更新UILabel的character count。



    如果通过代码来直接更新UITextView的文本,是不会有代理方法被调用的。
    这时候可能会出现什么问题?



    如果,此时只是通过代码来更新UITextView的文本,character count将得不到更新:



    所以,我们需要通过model来直接更新TextView的文本和character count,因为实际的数据在model处。



    如果用户输入了新的值,model也需要被更新。



    但是,如果只是这样做的话,我们就丢失了原始数据。
    假如,当前页面是主界面列表中某一条记录的详情页,用户通过点击主界面列表跳转到当前页面。如果我们直接对原始的模型进行修改,这是不合理的。



    所以,合理的设计应该是这样的:



    总结一下设计信息流的过程:

    • 找到真实数据在哪里
    • 找到真实数据和派生数据的联系
    • 更新真实数据




    定义明确的责任

    职责不明确 职责明确



    对输入数据进行校验是软件开发中常见的情况,尤其是对注册、登录模块的输入数据进行校验。



    常见的验证流程:

    • 对每个输入框进行验证
    • 当所有输入框的验证都通过时,将按钮设为可用状态。



    全部输入框的验证流程:

    • 检查用户名输入框
    • 检查密码输入框
      • 检查第二个密码输入框的值是否和第一个相同
    • 检查邮箱输入框




    某个输入框的验证流程:

    • 创建正则表达式
    • 查找匹配的用户名
    • 如果有匹配项,用户名有效
    • 否则,在输入框提示错误



    在这个过程中,我们还需要判定nil:




    然后,我们可能会写出这样的代码:

    NSString *username = [self.usernameField text];
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@“[a-zA-Z0-9_]{6,}” options:0 error:nil];
    NSRange result = [regex rangeOfFirstMatchInString:username options:NSMatchingAnchored range:NSMakeRange(0, [username length])];
    if (username && result.location == NSNotFound) {
       allValid = NO;
       [self.usernameField setBackgroundColor:[UIColor redColor]];
    } else {
       if (!username) {
          anyNil = YES;
       }
       [self.usernameField setBackgroundColor:[UIColor whiteColor]]];
    }
    

    请注意,验证过程直接对输入框的背景色进行了更改,这是不合理的。

    而且,验证过程被定义在了ViewController中,这也是不合理的。



    如果有多个页面需要进行相同的校验,一定会出现这样的情况:



    所以,我们应该将验证过程独立出来:



    验证过程的特征:

    • 接收输入数据
    • 检查输入数据是否有效
    • 如果无效,给出原因



    根据以上特征,定义一个验证器协议:

    protocol Validator {
        validateWithError(error: NSErrorPointer) -> Bool
    }
    



    这样定义有以下优势:

    • 由协议的遵守者来定义输入
    • 可以由小的验证来构建大的验证
    • 可以组合
    • 同样适用于ObjC



    然后,定义用户名验证器:

    class UsernameValidator: Validator {
      var input: NSString?
      func validateWithError(error: NSErrorPointer) -> Bool {
        let regex = NSRegularExpression(pattern: ...)
        ...
      }
    }
    

    然后,定义密码验证器:

    class PasswordValidator: Validator {
        var input: NSString?
        ...
    }
    
    class SetPasswordValidator: Validator {
        let firstPasswordValidator = PasswordValidator()
        let secondPasswordValidator = PasswordValidator()
        ...
    }
    

    然后,只需要组合前面的验证器就可以轻松定义注册验证器:

    class SignUpValidator: Validator {
        let usernameValidator = UsernameValidator()
        let setPasswordValidator = SetPasswordValidator()
        let emailAddressValidator = EmailAddressValidator() 
        ...
    }
    




    用不变性进行简化

    假设有三个实例 A, B, C:

    当你需要把A中的值赋给B时,如果这个值为引用类型,那么A仍然指向了这个值。

    如果,B对这个值进行修改,A也会受到影响。

    如果再将这个引用值赋给C,情况将会变得更加复杂。




    如果使用值类型,而不是引用类型呢?

    把A中的值赋给B。


    B如果改变这个值也不会影响A。





    Swift中的 struct 是很好用的值类型,它有以下特征:

    • 可选择的可变性(mutating 关键字)
    • 传递的是值,而不是引用 (Copy-On-Write 写时复制)




    参考内容:
    Advanced iOS Application Architecture and Patterns




    转载请注明出处,谢谢~

    相关文章

      网友评论

          本文标题:(WWDC) 高级iOS应用程序架构和模式

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