本文由CocoaChina译者@WhistlingArrow翻译
原文:Ziggurat iOS App Architecture
今年六月,我做了一场关于避免臃肿的ViewController的演讲,用Swift讲解了一种采用“单向数据流”的架构模式。当时并没有发布相关的博客,甚至没有给这个架构起个名字。现在两者都有了。首先介绍一下Ziggurat:它是一种通过不可变的视图模型和单向数据流来实现的分层的、易测试的架构模式。
这个架构的名字Ziggurat是根据阶梯状的金字塔得来的。像金字塔的阶梯一样,数据以数据流的形式单向地沿着App的层级传递,并逐层缩小。单向不可变的数据流可以减少认知负载,同时使类也变得更加瘦小。和Ziggurat相比,典型的MVC架构显得缺少了一些指导性,对数据和状态的操作可能散落各处,包括ViewController。
Ziggurat结合了大量讨论和文章中的概念,相关内容列在了下文中更多参考部分。(剧透一下:这个架构的灵感来源于之前Facebook关于React Native的讨论,并且它和Flux也很相似)。这里提及的这些架构模式与MVC相比都是一种更好的选择。
下面将具体介绍Ziggurat:阐述它的背景,权衡分析优缺点,定义它的组件,并通过一个例子来介绍它的用法。
MVC带来的问题Ziggurat是MVC的替代选择。MVC只是简单地将每部分的责任分离开,但并没有将每个角色和责任分离的很清晰适当。下面是MVC带来的常见问题:
- 臃肿的ViewController
ViewController负责处理数据、管理I/O、请求API、包含了Model类,以及其他超出它的范围的任务:管理视图及UI事件。
- 共用数据造成很难调试的Bug
谁应该处理数据?什么时候处理数据?如果不经过精心设计,那么答案将是谁都可以处理、什么时候都能处理。没有搞清这些会造成一些列的连锁效应。
- 太多的责任导致难以测试
没有采用单一责任原则时会使界限变得不明确且不易发现。
在实践中,由MVC缺少隔离导致的App没有一种清晰地设计模式使得测试和调试bug变得非常困难。对一份代码,不应该让工程师们通过将一个App的设计模式和数据模型做逆向工程的方式来提高工作效率。
描述组件和单向数据流这个例子使用了这个架构模式,并且所有的组件都有说明文档
上面例子的说明文档提供text格式文件
我的臃肿的ViewController演讲
Square工程师Kat Hawthorne的关于单向数据流的演讲和幻灯片,下面是我引用其中的一张:
上图直观描述了Ziggurat的单向数据流( 这里 是演示)
一个外部事件触发(例如用户输入)
ViewController通知服务器它收到了用户输入
服务器开始解析或确认此次输入并可能对改变其状态(只有服务器能改变其状态)。然后调用signal()方法
signal()方法通知渲染器开始更新
渲染器通知展示者,并生成一个View Model(服务器不会打断循环渲染过程)
渲染器将View Model 传递给View Controller
渲染循环等待下一个外部事件触发
Ziggurat架构模式解决了MVC App面临的常见问题:
- 臃肿的View Controller
多层的架构模式为所有功能提供了清晰的根节点,从而避免了臃肿的View Controller
- 共用数据造成很难调试的Bug
减少了无状态性和易变性:服务器层封装了易变的发生(将它限制在主线程上),并且展现者和渲染都是无状态的和单向的。使用依赖注入代替了公用状态和全局单例。
- 太多责任导致难以测试
更加瘦小的分层使责任分布得更均匀
带来的改进Ziggurat以很多方式优化了我们的生活,它使小团队的工程师们(从没有经验到几年经验)可以在很短的期限内完成任务。
首先,它通过给工程师在面对新代码库时提供指导和保护性措施的方式,清晰地定义了App的层级和角色。新工程师对它的上手时间很短,因为每个组件都定义的很明确并且符合人们的直观感觉。其次,Ziggurat模式非常易于测试。例如,可以用View Model层来将数据以目标结构输出。这点在MVC中是无法实现的。
单向数据流避免了MVC。不可变类型,更小的类(良好的结构),更多的分层减少了工程师们的精力消耗。它可以很简单地以轻松方式来运行你的App,因为View层中不必包含业务逻辑;所有的业务逻辑都是可测试的而不要View层;View的状态可以通过View Model层在某一时间点的快照来重新创建。最后,我们发现这使得App变得非常轻便。多亏了这种分层的方式和依赖注入,可以轻松实现代码移植。
带来的问题Ziggurat会使一些方面变得更加困难,比如动画。我们建议在不需要很多动画的App中使用Ziggurat。在View Controller中实现的动画会在将来调用有关更新的方法时遭到破坏,如果不小心控制这点的话会造成界面的闪动。在Flux中,使用了React Native来解决这个问题。
此外,虽然提供了编译期的检查,在逐层传递数据时还是会带来一些样板代码。并且信号数据在传输时会存在潜在性的瓶颈。与React不同,属性变量不是KVO的;而是通过View Model的方式来更新属性。这里可能需要做一些优化,比如减少一些不需要的渲染或是调整一下你的View Model。
依赖注入需要小心地设计。我们最初的方法就导致了一张很大的由类组成的网。我们用了一些循环的依赖,导致最后不得不重构。
结论在新项目中有机会尝试新的想法。MVC存在很多明显的劣势。与此同时,一些主题的讨论比如Flux、React Native、广泛地使用Swift的值类型等正在业界形成了一股势头。
我们混合使用了上面的想法,用Swift写了程序。我们感到很兴奋对于使用单向数据流带来的效果:可测试性、依赖注入、轻量级的View Controller、值类型的View Model。
这是关于Ziggurat权衡优缺点的分析,但到目前为止它仍可以很好地服务于我们。我们希望你在下一个项目中考虑采用一些这些想法(或者全部采用!),尤其是在你打算使用MVC之前。
更多参考我看了早期的关于React的预告片,它们是Ziggurat灵感的直接来源。Flux和React Native使用了相同的概念并且是通过JavaScript实现的。
Flux是一种很强健的,以action为中心的设计模式,并且可以结合React Native一起使用。Ziggurat在涉及动画较少的App中使用会非常容易,(Flux同样存在这个问题,但React Native中解决了这个问题)
我们希望你在下列文章中找到Ziggurat模式的有益之处,以及一个可以代替MVC的更好方案:
OS Architecture Patterns
MVVM is Not Very Good
Introduction to MVVM和MVVM in Swift
Controlling Complexity in Swift
Introducing React Native
Flux(andtalk)
Architecting iOS Apps with VIPER
Simple static table views for iOS in Swift
感谢Ruby Chen的插图
更多译者翻译文章,请查看:http://www.cocoachina.com/special/translation/
本文仅用于学习和交流目的,转载请注明文章译者、出处和本文链接。
感谢博文视点对本期活动的支持
I 相关 / Other
简介LinkedList 是一个常用的集合类,用于顺序存储元素。 LinkedList 经常和 ArrayList 一起被提及。大部分
本文由玩赚乐(www.banghui.org)– 小峰原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划!
在最近解决某个问题的时候,发现在ViewDidDisappear中去获取self.navigationController为空。猛然间意识到,
商办秩函〔2016〕21号各省、自治区、直辖市、计划单列市及新疆生产建设兵团商务主管部门:为加快推进重要产
1 月 24 日,腾讯宣布 1 月 8 日上线的《火影忍者》手机游戏至今已经获得了超过 1000 万的注册用户。 这个