许多面孔的VIPER-第1部分

我最喜欢的是蓝色。 如此宏伟的生物……。

虽然如此,尽管他是一位伟大的自然爱好者,拥有所有的生物,但我对动物学的了解不足,不会让我进一步感到羞耻。 当然,我将谈论VIPER客户体系结构。

这个故事不是另一个“ iOS上的VIPER简介”或当前其他任何地方。 我相信我们有很多有关该主题的文章。 假定您了解该概念并具有一定的经验。 这也不是要将其与任何其他架构方法或架构进行比较。 我之所以选择它,是因为它确实代表了一种相对较好的“ 清洁体系结构”方法,并且遵循SOLID原则,可以承受。 我什至不会自己使用首字母缩写词,但是我想它使我们所有人都更容易理解该主题。

这个故事是关于以更多样化的方式展示VIPER,并展示其友好的面孔。

为此,我将首先处理一些神话,然后再讨论一些问题,考虑真正的问题,下一次我们将研究一个演示项目,该项目将作为一个可能的解决方案/答案。

误区1:VIPER有很多课程

可以? 您认为应该有几节课? 或更准确地说:您希望在代码中包含多少“ 单一职责原则”

也许所有这些都值得商bat。 即使采用SR原则,我们也可以很快就某个特定类的职责进行辩论。 例如,Microsoft在MVVM的定义中将视图模型视为胶水代码 ,作为业务逻辑和视图之间的中间人。 有人可能会争辩说,这种职责范围太广:处理事件,表示逻辑并将模型数据转换为易于查看的形式应视为单独的职责。

关键是:无论采用哪种体系结构,如果要使代码可维护且可靠,那么它将必须符合“干净代码”的许多范式,其中包括SR,小类,尽可能少的耦合,OO模式,全部资金。 ……所有这些都导致“……很多课程……”

误解二:僵硬,有很多样板代码

我记得几年前,当我第一次接手VIPER并接管现有项目时,这并不是最好的体验。 不是因为VIPER本身,而是因为与该概念的明显斗争。

我实际上看到了我在这里所说的一个神话:

  • 每个模块都需要很多样板代码
  • 很多时候,这些类都没有创建,只是为了“遵循方法”
  • 开发人员努力地采用需求的逻辑以使其适应VIPER规范,这当然是行不通的,并导致一些严格的遵循并同时破坏它的怪异组合。当我说“破坏”时,我真的这意味着从破坏模块的封装为一的角度来看。
  • 演示者路由器双向连接太多对象,并使每个结构更改都成为真正的PITA。

我们将在稍后的演示项目中看到,如果您牢记这一点,则不必一定是这样:

  • 您需要遵循的是VIPER所基于的SOLID和其他OOP原则,并据此,您应该创建适合您工作的类。
  • 尽可能在协议扩展,抽象类甚至父类(如果不是太多!!)中实现样板

误区3:学习困难,仅适合高级开发人员

VIPER就像学习SOLID,TDD和OOP设计模式一样困难。 等等…但这还是我们要做的,不是吗?

好的,我也可以添加FRP 。 但是没关系,您的意思是:如果您想成为一名真正的高级开发人员,那么您必须学习上述知识,在这种情况下,您应该编写VIPER编码就像将比萨饼切成小块并撒上手工啤酒一样简单。选择。

是的,但是是初级人员……好吧,那么,如果您的初级同事没有采用VIPER风格,那么您的代码库将采用初级人员的技能组吗? 如果是这样,那么是的,VIPER仅适合高级开发人员。 但是,在我曾经工作过的大多数地方,我们都确保初中生由年长者通过代码进行培训,而并非相反:采用代码以适应最高的通用技能水平……

误解四:与SDK规定的MVC基础架构配合使用时,效果不佳

我想我在面向模块的体系结构系列中再次揭穿了这一点,特别是在第6部分:超越MVC中。 您也有类似Uber RIB的框架,也可以解决该问题,但是我不得不承认,我个人认为不应将体系结构转变为即时框架解决方案,但这是重点。

误解5:不适合小型项目或原型

同样,在面向模块的体系结构系列中,我们可以看到VIPER类完全精简和小巧,因为大多数样板都在协议扩展中实现。

如果您遵循神话2中所述的方法,那么即使在小型项目和原型中,您也会发现它非常易于使用并且可以快速实现任何种类的特定功能。

问题1:对象之间的通信会变得复杂而复杂。

我认为这是一个真正的问题……

我已经在我从事的第一个项目中提到过问题。 我记得委托,闭包,连线,全局变量的各种组合,以及您可以想象的各种事物。 许多模块被大量的内存管理问题以及随后的竞争状况所困扰。

但是,这是怎么发生的呢? 问题是编码技巧吗? 对VIPER,SOLID的理解不佳? 这些都没有道理……

我花了很长时间才找到了罪魁祸首,这真是好笑,特别是当我根据这个发现开始编码时,发现根本原因是多么简单明了……

那是什么 通常的嫌疑犯:来自SOLID的S:单一责任原则!

是的,这些模块太大,太全面了,无法做太多事情。 他们没有提供任务,服务,而是提供了功能,很多时候直接与产品功能相关。

这导致以下情况:

  • 更多责任创造更多班级
  • 更多的类在它们之间创建了更多的依赖关系
  • 需要更多的任务及其类,并从其他模块创建对类的更多依赖性,从而产生更多潜在的内存问题,并使整个应用程序更加耦合和僵化,从而浪费了VIPER的基本收益之一:模块封装的任务和功能
  • 更多的类和依赖项,更难测试,更难发现潜在问题。
  • 更多的任务,对业务逻辑类的更多要求超出了通常的VIPER 5,这给如何将它们与其他对象连接带来了更多困惑
  • 更多的业务逻辑类,类之间的更多双向通信,这增加了创建内存问题的风险,尤其是当涉及具有自己的内存管理的UI导航时
  • 要么…。
  • 在VIPER 5类中,更多的职责也创造了更多的功能,从而在类级别突破了SR原则,从而带来了所有已知的共识。

结果是严峻的,尤其是考虑到原因如此简单和明显。

解?

但是,原因很简单,解决方案却不那么容易。 我们将再看到3个简短的部分:

  • POC项目将展示VIPER的潜在多样性,解决上述一些神话
  • 我们将使用相同的项目来演示如何将“干净架构的用例”应用到App层上并编排应用程序,并使模块保持精简和高度可重用性。 我们已经在模块架构中引入了Application Services,这将是进一步的发展。
  • 我们将看到,即使在一个非常精简的模块中,对象之间的链接仍然仍然是一个问题,我们将使用基于事件的方法来解决它。

在这里留下一些有关我们的演示项目的信息:

它几乎是一个单一视图的银行应用程序,我们在首页上列出了一些主要亮点最后付款

也可以通过点按任意一个来突出显示突出显示 ,但是稍后在我们将添加用户分析时,这将派上用场。 连续点击可扩展和折叠“主要亮点”的显示。

在屏幕的下部,我们显示了上次付款 ,还可以看到详细信息。 选择付款,导航至“ 付款明细”

这是所用类的模式,仅用于预告片…

我们已经看到了几样东西,这表明我们有选择的自由和拒绝僵化,还有一个也是唯一的VIPER模块模板:

  • 我们也使用View Models ,因为我们想保持模型对象整洁,因此Interactor或多或少着重于与API和Presenter的通信以提供视图而不为它们创建内容
  • 如上所述, Presenter角色只是表示逻辑 ,它通过调用Interactor从中获取模型并将其映射到视图模型中来单方面响应视图控制器请求视图模型。 如果会有更复杂的视图,那么最后一个任务很可能会转移到一个特殊的View Model Factory中,该工厂将为Presenter完成这项工作。 但是,由于我们的案例很简单,因此我们不会仅出于“为了该方法”而创建类。
  • 对于高光,我们使用两阶段视图模型,其中每个高光都有一种类型,而所有高光都有一种类型,以使扩展/折叠状态远离Presenter
  • 我们使用嵌入式的Table View Controller进行付款,也将其用作Table View数据源,我们认为不需要单独的类型
  • 我们总共有3个View Controller和一个Presenter !!! aAaaaaaaaaggggrrrhhh !!! 触及VIPER的圣杯! 好吧,……不。 演示者仍在那儿,处理演示逻辑,说实话,第二个视图控制器只是一个嵌入式视图,目的是与父视图集成在一起,第三个视图控制器是数据的简单详细视图,已经由演示者获取,因此后者引用视图模型
  • 付款明细视图控制器没有单独的视图?! 不,这么简单就不需要它,self.view可以做到,但是我们将在下一章中看到代码。

我们发现了一些潜在的VIPER陷阱,揭穿了一些神话,然后我们提出了一个解决方案,该解决方案没有盲目地遵循该概念,而是在VIPER与现实之间架起了桥梁。

下一章将介绍我们的POC项目,序列图和代码。

敬请关注!