如何选择合适的iOS体系结构(第2部分)

MVC,MVP,MVVM,VIPER或VIP

您可以在这里查阅第一部分。

最重要的iOS架构

简要概述。

MVC

MVC层如下:

M:业务逻辑,网络层和数据访问层

V:UI级别(UIKit对象,情节提要,Xib)

C:协调模型和视图之间的中介。

要了解MVC,我们需要了解它的发明背景。 MVC是在旧的Web开发时代发明的,当时View没有地位。 在过去,每当我们需要对网站进行视觉更改时,浏览器都会重新加载所有HTML。 当时,还不知道视图状态将被持久保存。

例如,有些开发人员使用相同的HTML文件,PHP和数据库访问权限。 因此,MVC的主要动机是将视图级别与模型级别分开。 这增加了模型级别的可测试性。 据说在MVC中,视图层和模型层不应该彼此了解。 为了实现这一点,发明了称为控制器的中间层。 这就是所应用的SRP。

MVC周期的一个示例:

  1. 触发了视图级别的用户操作/用户事件(例如“更新操作”),并将此操作传达给控制器
  2. 将数据发送到模型级别的控制器
  3. 将返回的数据建模到控制器
  4. 控制器表示视图将使用新数据更新其状态
  5. 查看更新其状态

苹果MVC

在iOS中,视图控制器与UIKit和生命周期视图结合使用,因此它不是纯MVC。 但是,在MVC定义中,无话可说,控制器无法了解特定于视图或模型的实现。 其主要目的是将模型级别的职责与视图级别分开,以便我们可以重用它们并单独测试模型级别。

ViewController包含视图并拥有模型。 问题是我们正在将控制器代码和视图代码都写入ViewController。

MVC通常会导致所谓的Massive View Controller问题,但它只会在具有足够复杂性的应用程序中发生,并成为一项严肃的业务。

开发人员可以使用几种方法来使视图控制器更加清晰。 一些例子:

  • 提取其他类的VC逻辑,例如表视图方法的数据源,并使用委托设计模式为其他文件委托。
  • 创建更清晰的组合职责细分(例如,将VC拆分为子视图控件)。
  • 使用协调器设计模式来消除在虚拟控制器中实现导航逻辑的责任
  • 使用DataPresenter包装器类,该包装器类封装逻辑并将数据模型转换为表示提供给最终用户的数据的数据输出。

MVC与MVP

如您所见,MVP的图与MVC非常相似

MVC是向前迈出的一步,但仍然以某些事情的缺席或沉默为标志。

同时,万维网正在发展,开发人员社区正在发展许多东西。 例如,程序员开始使用Ajax,并且只加载部分页面,而不是一次加载整个HTML页面。

我认为在MVC中,没有迹象表明控制器不应该知道View的具体实现(不存在)。

HTML是视图层的一部分,很多情况都是愚蠢的。 在某些情况下,它仅接收来自用户的事件并显示GUI的可视内容。

随着网页的各个部分被装入各个部分,这种分段导致了视图状态的保留,并且更加需要分离表示逻辑的职责。

表示逻辑是控制用户界面如何显示以及用户界面元素之间如何交互的逻辑。 一个示例是控制逻辑,该逻辑在何时应该开始显示/动画显示加载指示符以及何时应该停止显示/动画显示。

在MVP和MVVM中,视图层应该愚蠢到不包含任何逻辑或智能,而在iOS中,视图控制器应该是视图层的一部分。 View愚蠢的事实意味着即使表示逻辑也保留在View平面之外。

MVC的问题之一是表示逻辑应该去哪里还不清楚。 他只是对此保持沉默。 表示逻辑是在视图平面中还是在模型平面中?

如果模型的作用是仅提供“原始数据”,则意味着视图中的代码如下:

请考虑以下示例:我们有一个用户,名字和姓氏。 在视图中,我们必须将用户名显示为“姓氏,名字”(例如“ Flores,Tiago”)。

如果模型的作用是提供“原始数据”,则意味着视图中的代码如下:

让firstName = userModel.getFirstName()让lastName = userModel.getLastName()nameLabel.text =姓氏+“,” +名字

这意味着View负责处理用户界面逻辑。 但是,这使得无法对用户界面逻辑进行单元测试。

另一种方法是让模型仅显示需要显示的数据,并从视图中隐藏业务逻辑。 但是,我们有了处理业务逻辑和用户界面逻辑的模型。 这将是一个可测试的实体,但是该模型将隐式依赖于视图。

让名称= userModel.getDisplayName()nameLabel.text =名称

对于MVP来说这很清楚,并且呈现逻辑仍处于呈现者级别。 这增加了演示者级别的可测试性。 现在可以对模型和演示者层进行测试,而不会出现任何问题。

通常,在MVP实现中,视图隐藏在接口/协议后面,并且在演示者中不应引用UIKit。

要注意的另一件事是传递依赖。

如果控制器将业务层作为依赖关系,并且业务层将数据访问层作为依赖关系,则控制器对数据访问层具有可传递的依赖关系。 由于MVP实现通常在所有级别之间使用协定(协议),因此没有传递依赖项。

不同的层也由于不同的原因和速率而改变。 因此,如果您切换一个级别,则不应在另一个级别上引起次要效果/问题。

协议比类更稳定。 日志不包含任何实施细节,并且未链接到合同。 因此,可以改变一个级别的实施细节而不影响其他级别。

合同(协议)在层之间创建了去耦。

MVP与MVVM

MVVM图

MVP和MVVM之间的主要区别之一是,在MVP中,主持人与视图交互,而在MVVM中,视图着重于数据和事件更改。

在MVP中,我们使用接口/协议在演示者和视图之间创建手动链接。 在MVVM中,我们使用RxSwift,KVO或具有泛型和闭包的机制执行自动数据绑定。

在MVVM中,我们甚至不需要ViewModel和View之间的合同(例如Java接口/ iOS协议),因为我们通常通过观察者设计模式进行通信。

MVP使用委托模式,因为演示者层将命令委托给视图层。 因此,他需要了解一些有关视图的信息,即使只是接口/协议签名也是如此。 考虑一下Notification Center和TableView委托之间的区别。 通知中心不需要任何接口即可创建通信通道。 但是,TableView Delegates使用了类应实现的协议。

考虑一下电量指示器的表示逻辑。 在MVP中,演示者运行ViewProtocol.showLoadingIndicator。 在MVVM中,ViewModel中可能存在isLoading属性。 视图层使用自动数据绑定来识别此属性何时更改和更新,因此MVP比MVVM更具吸引力,因为演示者发出命令。

MVVM不仅仅是直接命令,更是关于数据更改的,我们将数据更改链接以查看更新。 当我们将RxSwift和功能性反应式编程范例与MVVM一起使用时,我们使代码的吸引力和声明性降低了。

MVVM比MVP更易于测试,因为MVVM使用观察者设计模式,该模式以分离的方式在组件之间传输数据。 因此,我们可以通过比较两个对象来查看数据中的变化,而不必模拟方法调用来测试视图与演示者之间的通信,从而进行测试。

PS:我对该商品进行了一些更新,使其变得更加强大。 因此,有必要将其分为三个部分。 您可以在此处阅读第三部分。

第二部分到此结束。 欢迎所有反馈。 第三部分是关于VIPER,VIP,反应式编程,权衡,约束和上下文感。

感谢您的阅读! 如果您喜欢这篇文章,请拍拍,以便其他人也可以阅读:)