适用于iOS应用程序的VIPER架构的优点,缺点和缺点。

VIPER介绍

如果您开发iOS应用程序已有一段时间,您会注意到大多数逻辑都放在UIViewController的子类中。 通常将其称为Massive ViewController问题。 视图是一个简单的对象,仅关心在屏幕上绘制自己。 模型是无用的数据存储,可以用简单的集合或复杂的数据结构表示。

基本上,所有其他逻辑都应该放在这个可怜的孤独ViewController中。 它将模型对象转换为更多对视图友好的状态,反之亦然。 它还处理用户输入,获取数据,进行网络调用,分配和显示其他ViewController。 所有这些乐趣都归于ViewController是否公平?

不仅ViewController的可读性受到额外的负担(在代码行中),而且可测试性,可维护性和重构速度也受到影响。 考虑到Swift瞬息万变,Apple每年都会发布新的iOS,因此维护和重构ViewControllers是您作为iOS开发人员的重要工作。 可测试性在这里非常重要,因为您不希望那些可怕的回归。

因此,一些聪明的人决定以ViewController基本节食的方式改进MVC(模型-视图-控制器)。 MVVM(Model-View-ViewModel)是第一次尝试。 它使ViewController无需在模型对象(如果愿意,可以选择业务对象)之间转换为对视图友好的对象(从现在起将它们称为ViewModels)即可。 这种方法提高了可测试性,并使ViewControlles更加精简,但这是否足够? 它不涉及对动作,网络,导航等的响应。 因此人们决定他们还需要从其他东西中释放ViewController。

首先,他们创建了Presenter (在MVVM的情况下为ViewModel)。 此类负责更新用户界面。 它接收业务对象并将其转换为ViewModels。

它从哪里接收它们? 来自另一个称为Interactor的类,该类负责从任何可能的地方获取业务对象:网络,持久性存储,并将它们传递给Presenter。 它还负责响应用户的操作。 因此,当您按下登录按钮时,ViewController会将其传递给Interactor并进行网络调用(通常,您想在一个单独的对象中进行此操作,更多信息在下面的内容中),等待响应,然后确定下一步需要执行的操作。

因此,当Interactor做出此决定时,它将调用另一个称为Router的对象(您可能会在某些文章中找到Wireframe ),该对象执行监视或以模态形式显示ViewController。

因此,您可能会问ViewController还剩下什么? 除了与Presenter通信外,它还可以管理很多事情。 由于我们在UIKit的领土上玩游戏,因此我们无法摆脱它的影响。 我相信您应该将ViewController分配为各种事物的委托和dataSource。 这样,您就不会在Interactor上做一些疯狂的事情 不要试图破坏UIKit。 这是不可能和不必要的。

因此,我们有V表示视图I表示交互器P表示主持人R表示路由器 。 那么VIPER中的E代表什么? E代表Entity 。 实体基本上就是您在MVC中所说的模型 -枯燥的数据存储。 就我个人而言,我觉得将它放在VIPER中只是为了使其听起来很酷。 您的Interactor将从各种来源获得这些实体 ,但还将使用Services (封装此类任务的对象)进行网络调用或执行一些长期任务。 例如,您可以在ToDoList应用中创建TasksService (每个iOS开发人员都应该创建一个) ,该应用将对服务器进行网络调用,将响应解析为业务对象,然后将其返回给Interactor 。 假设您会在响应中找到fireDate。 它将类似于以下格式: “ 1994–11–05T08:15:30–05:00”,但在“ 视图”中应显示类似“剩余2小时”的信息。 互动者 将Date对象传递给Presenter ,该Presenter将Date转换为String并将其传递给ViewController。

但是我们在这里缺少一些关键要素。 您的每个ViewController都需要Interactor,Presenter和Router。 所有这些东西都通过广泛使用协议相互通信。 因此,您需要集合所有这些家伙,并使他们彼此了解。 这是通过称为ConfiguratorAssembly的对象完成的

因此,现在我们拥有创建VIPER体系结构的一个孤立部分(称为模块)所需的一切。

您应该知道,没有通用的VIPER模块之类的东西。 一些开发人员将RouterPresenter而不是Interactor连接起来,其他开发人员使用与Entities一起使用的单独对象,等等。

我们已经使用VIPER大约3个月了。 您可能会说,时间不是很长,但是足以看到一些陷阱。

善良

  • VIPER尝试将SOLID原理应用于iOS平台。 它不仅是架构,而且是一组规则和协议。 而且非常有帮助。 一旦习惯了,就不会后退。 只要尝试一下,您就会喜欢它。
  • 开箱即用的可测试性。 由于模块是松散耦合的,因此分别进行测试非常容易。 模块内部的每个对象也被很好地分开,这对于单元测试非常有用。 与几乎无法测试的MassiveViewController相比,可以轻松实现80–90%的测试覆盖率。
  • 由于模块是独立的,因此VIPER对于大型团队确实非常有用(在iOS中,它有两个以上的开发人员)。 这意味着更少的合并冲突,更好的可测试性和更容易的模块更改。 您可能想要先创建初始体系结构框架,然后再将模块逐个交给其他开发人员以实现逻辑。
  • 代码库看起来很相似。 只要您开始感受到VIPER的理念,阅读别人的代码就会更快。 文件将更小(不再有3K行代码UIViewControllers)逻辑将更清晰,整体稳定性和灵活性更高。 您甚至可以与Android团队进行业务逻辑的跨平台代码审查。 多么酷啊!

坏人

  • VIPER不适合Apple的UIKit范例 。 您不可能在Interactor和Presenter之间轻松拆分业务和表示逻辑。 第三方SDK也常常不适合VIPER。 因此,您必须与同事讨论很多事情,尝试不同的方法并进行实验。 这实际上对工程团队来说听起来很有趣,但是请尝试将其出售给产品团队。
  • 在文档,最佳实践和社区方面几乎没有。 除了几篇文章,您所拥有的只是用俄语写的《 VIPER图书》 (目前只有10%的译文被翻译成英文),而没有别的。 自从我能够找到的第一篇关于VIPER的文章发表于2013年以来,您可能会认为它不是那么受欢迎。
  • 新开发人员应该花几天的时间学习VIPER的基础知识,然后花两周的时间来熟悉它。 每个请求请求都有额外的时间,更多的时间用于研究和讨论。 最后,所有这些问题都不会消失。 一段时间后,您将获得一些协议和操作方法,但是总会有新的挑战。
  • View,Interactor,Presenter,Entity,Router,Configurator …您可以猜到它包含很多文件 。 测试两次。 每次手动设置它们都是浪费时间。 因此,聪明人为Gener等VIPER创建了自动代码生成器。 它不仅可以帮助您生成所有文件,还可以帮助您生成协议和通用功能。

丑陋的

  • 模块之间的通信。 在经典VIPER模块中,应与演示者进行交流。 我不同意这种概念违反单一责任。 由于VIPER的重点是独立的模块,因此我们需要找到一种在模块之间传递数据的方式,而无需了解有关其实现的任何细节。 解决此问题的最佳方法可能是ViperMcFlurry。 但是,如果您查看其代码内部,将会看到它是基于方法转换和关联对象的。 难道不是这里有些事情太复杂了吗?
  • 最终,您将必须构建适合您需求的VIPER版本。 一段时间后,您会注意到VIPER体系结构中的一些缺陷:某些模块不需要View,Presenter仍未使用,等等。Uber的家伙做了出色的工作,创建了RIBs体系结构。

而不是勾结

VIPER在纸上看起来很出色:SOLID原则,TDD,代码分离,可扩展性等等。 但是我们生活在现实世界中。 我们有最后期限,我们需要快速迭代并尽快将产品交付给客户。 您可能会说这是产品经理,设计师甚至客户的错。 我部分同意这一点。 作为开发人员,我们应该教育管理人员,确保速度≠质量,迟早的技术债务将成为问题。 这就是为什么我没有在“不良”部分中列出该原因。

当您有足够的时间并且应用程序变化不大时,VIPER确实很棒。 我强烈建议您尝试VIPER。 之后,您将成为更好的开发人员。 在您的辅助项目中,也许在一些小型助手应用程序中。 刚开始时它会慢很多,但是与MVC相比,一段时间后您将节省(!)时间。 然后,在您的工作场所开始使用VIPER。

如果您有任何疑问,请点击此处或在此处回复。