资讯专栏INFORMATION COLUMN

使用 FLOWCONTROLLERS 改进IOS应用架构

wmui / 3251人阅读

摘要:每个都定义了可以监听它们的行为的接口如果需要支持多个设备和不同的展示方式,程序中会有其他类,没有意大利面条的代码。使用这样的架构的好处是显而易见的界面之间没有依赖关系。更干净的代码,我看到的唯一意大利面条代码是我做的。

引言

开发iOS应用时,现在更应该避免在一个视图控制器中直接展示其他视图控制器。

为什么?

问题

现代应用程序通常需要需要支持以多种方式展示相同的视图控制器。例如,在iPhone上你 push 一个新的视图控制器,但是在 iPad 上,你会把它嵌入另一个视图控制器或者用 popover 展示出来。

另外,很多情况下,你可能想在不同的情景中重用同一个视图控制器。如 UIImagePickerController 可以在多个地方以不同的方式展示出来。

视图控制器应该不依赖于他们的展示样式,这就是 SizeClasses 出现的原因之一。

如果你从其他 VCs / ViewModels 展示视图控制器,你将写出来一堆if语句,你的代码将变成条件大面条(big spaghetti of conditions)

我作为一个顾问,经常需要参与审查项目,并帮助团队制订更干净的解决方案。

我看到过很多的意大利面条代码。下面这个就是相当糟糕的一个例子,但是还没有接近我见过最差的:

func doneButtonTapped() {
  let vc = NextViewController(prepareNeccesaryState())

  if Device.isIPad() {
    navigationController.pushViewController(vc, animated: true, completion: nil)
  } else {
    var nav = UINavigationController(rootViewController: vc)
    nav.modalPresentationStyle = UIModalPresentationStyle.Popover
    var popover = nav.popoverPresentationController
    popoverContent.preferredContentSize = CGSizeMake(500, 600)
    popover.delegate = self
    popover.sourceView = self.view
    popover.sourceRect = CGRectMake(100, 100, 0, 0)

    presentViewController(nav, animated: true, completion: nil)
  }
}

这个幼稚的实现存在很多问题:

不必要的依赖 —— 一个视图控制器不需要知道另一个

可重用性差

面条代码,如果你的应用需要以不同的方式展示视图 - 你需要写大量的控制流。

单例是诱人的,因为它们让你更容易编写代码。

测试更难,你的VC / VM会有很多的副作用。

我们怎样才能解决这个问题? 清理你的 ViewControllers / ViewModels

这可以应用到 MVVM,MVC 和许多其他的常见模式。当我谈到 VC / VM,思考一下你现在正在使用的那一个。

让我们使用代理或者基于 block 的接口,而不是对相关的控制器硬编码,来摆脱所有的依赖关系。

class MyViewController {
  let onDone = (Void -> Void)?

  func doneButtonTapped() {
    onDone?(prepareNeccesaryState())
  }

}

ViewController / ViewModel 应该:

不能引用其他界面

不使用任何 UIKit presentation 类或类似 UINavigationController 或 presentViewController 的方法

有允许其他的对象通过注册来获知这个功能正在运行的接口,例如,代理或 block

不引用任何单例,稍后你会看到用我的做法是如何容易实现这一要求的。

这个时候,我们已经提高了可测试性,因为我们现在可以测试我们的接口是否被触发了,且无需副作用。伪代码:

let vc = createVC()
var executed = false
vc.onDone = {
  executed = true
}
//! add code here to trigger done state
expect(executed).toEventually(beTruthy())

但是,我们如何协调我们的应用程序视图控制器?

介绍 FlowControllers

一个 FlowController 是一个简单的对象,它将管理你的应用程序的一部分,我喜欢把它看成用例的一个子集。

FlowController 的三个主要角色是:

为视图控制器配置特定上下文 - 例如分别为从应用 CreatePost 界面弹出的 ImagePicker 和改变用户头像时弹出的设置不同的配置

监听每个 ViewController 中的重要事件,并用来协调它们之间的流程。

为视图控制器提供它需要的东西,从而移除VC中的单例

func configureProgramsViewController(viewController: ProgramsViewController, navigationController: UINavigationController) {
    viewController.state = state
    viewController.addProgram = { [weak self] barButton in
        guard let strongSelf = self else { return }
        let createVC = R.storyboard.createProgram.initialViewController!
        strongSelf.configureCreateProgramViewController(createVC, navigationController: navigationController)
        navigationController.pushViewController(createVC, animated: true)
    }
}

常见带有 FlowControllers 的应用架构像这样:

每个应用程序都有至少一个 FlowController,Root FlowController 由 AppDelegate 创建。

*实际上是 AppDelegate 中的一个 ApplicationController 创建了它,作为一个经验法则,你永远不应该引​​用你的AppDelegate,永远。*

每个 FlowController 可以有子控制器。

*如果您的应用程序具有可被看作是一个整体,需要多个屏幕的用户故事的一些重要的子集(如创建新的锻炼计划),那么你可以为那一部分创建一个新的子控制器,并从主控制器展示它。*

VC / VM 不知道其他 VC / VM。

*这意味着他们可以在任何地方重复使用,如果一个步骤是从导入用户照片库里的东西,你可以在应用程序的不同部分重复使用这段代码,例如,EditProfile可以使用相同的选择器选择用户头像。*

流量控制器配置和协调不同的界面。

每个 VC / VM 都定义了可以监听它们的行为的接口

如果需要支持多个设备和不同的展示方式,程序中会有其他 FlowController 类,没有意大利面条的代码。

这个想法最初是 Jim 和 Sami 一年前介绍给我的,我们经常使用它。

尽管我们的应用剧烈改变了3次,我们的架构都轻松地应对了,我们能够重复使用大量的代码,也有不少控制器不需要任何改变。

使用这样的架构的好处是显而易见的:

界面之间没有依赖关系。

高复用率。

更简单的代码注入并移除了单例。

更干净的代码,我看到的唯一意大利面条代码是我做的。

以更有表现力的方式来导航。

能够在共用大多数代码时对不同的设备编写不同的流。

可以轻松分离测试每个 VC / VM,因为一切都可以注入。没有必要子类化。

现在,在一些架构中也有类似的概念,如 VIPER 有路由器。但它们通常是很复杂的,需要大量的前期成本,以适配到现有的应用程序中。

这个方法最棒的地方是它很简单直观,立刻就可以(在现有的项目中)使用它,无需等待新项目。它在小型和大型项目的效果一样好。

不管你使用 MVVM,MVC 还是其他模式,如果应用中存在界面跳转,不妨试一试。


作者信息

原文作者:Krzysztof Zabłocki
原文链接:http://merowing.info/2016/01/improve-your-ios-architecture-with-flowcontrollers/
翻译自 MaxLeap 团队_UX成员:Alex Sun
翻译首发链接:https://blog.maxleap.cn/archives/879

商业转载请联系作者获得授权,非商业转载请注明作者信息与出处。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/11749.html

相关文章

  • 移动端开发:架构那点事!

    摘要:移动精英开发社群的第期,也是围绕架构这个话题进行讨论。本次我们希望结合实际开发中遇到的问题,来聊聊移动端的架构设计。这样的模式改进一些,可能会更适合移动端架构。潘卫杰之前我们公司移动端的大项目就是插座式开发的,批量出各个行业的。 此前,58 同城的技术委员会执行主席沈剑在 OneAPM 的技术公开课上分享过一个主题,「好的架构不是设计出来的,而是演技出来的」。因为对很多创业公司而言,随...

    KnewOne 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<