美文网首页
(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