- 原文链接:Brigade’s Experience Using an MVC Alternative
- 原文作者: Ryan Quan
- 译文出自:开发者前线 www.devtf.cn
- 译者:Quzhiyu
- 校对者:lastdays
- 状态:完成
#Brigade’s Experience Using an MVC Alternative
任何iOS开发者都会告诉你iOS应用程序是建立于模型视图控制器(MVC)设计模式。他们也可能告诉你有些人叫它“Massive View Controller”设计模式。因为,你会经常发现你的代码有明显的不适合视图或者模型,所以它被抛到了控制器,导致臃肿的控制器很难维持,因为你有一个巨大的类做的一切。
在发展中旅的iOS应用程序,我们想建筑师以这样一种方式,干净利落地解决了几个关键点:
- 易于迭代
- 友好合作
- 分离关注
- 易于测试
我们偶然发现了一个建筑叫VIPER,这似乎刚好符合要求。VIPER是发展与朋友之间手机互动,他们有一个很好的介绍博客的职位以及更多参与的博客。我们决定离开苹果的标准给VIPER一枪。
#什么是VIPER,我们要怎么使用它。
正如它的创造者所说, VIPER提供指导,构建应用程序的架构和可调整以适应个人应用。这将是一个VIPER是什么讨论我们如何调整以适应我们的需要。
为了最好的理解VIPER,忘记所有你知道的MVC。VIPER是一个新型的野兽(或者爬行动物),如果你的思维仍在MVC的地方,你会很难接受VIPER。想象你从起点开始,不知道iOS应用城西的结构。没有MVC。
VIPER打算分离应用程序几个角色之一的关注:
- 视图/用户界面
- 交互器
- 提出者/事件处理程序
- 实体
- 路由器/线框
注:其中一些另类的名字,你会看到如果你看看上面提到的其他VIPER参考上文的博客文章。我已经添加了这两个名字在这里头。
在实践中,我们发现,我们使用了一些额外的角色,增加了数据管理器和服务层。
下面是一个图一个典型的VIPER“栈”(稍后)在我们的应用程序和描述了我们如何看待VIPER。每一个方块代表一个单独的类,每一行代表一个参考实例之间的各自的类。
把每一个类当作一个工人在流水线中思考。每个类只知道如何进行一组有限的操作,并依赖于其他类来帮助完成手头的任务。
让我们先去把每个类的责任,然后我们会给出一个例子,从用户交互,向用户显示数据。
#视图/用户界面
查看责任:
- 向用户展示信息
- 检测用户交互
该视图是由提出者告诉由什么来显示,它告诉提出者,当事件需要发生。
#向用户展示信息
如果视图应该向用户显示错误,提出者可能会调用视图的方法:
然后,它可以显示错误,如它所喜悦的,它可以在一个警告视图,标签,或任何其他方式。这里的关键问题是,提出者不关心如何显示错误的细节。它只是关心它会显示。
###Detecting user interaction
###检测用户交互
如果一个事件发生如用户点击“登录”按钮,视图可能会调用一个类似的方法:
正如我们以前所做的那样,该对象的方法只是在有秩序下不干涉任务给下一个工人,一旦它完成了它的任务。就这一观点而言,它完成了它的工作来检测用户的交互,现在是向演示者处理事件。
#演示/事件处理程序
提出者职责:
- 告诉视图该显示什么
- 处理事件
提出者会告诉视图该显示什么,并相应地处理相应的事件。
###Telling the view what to display
###告诉视图该显示什么
正如我们刚才看到的那样,一个演示者的职责就是告诉我们要展示什么。为了完成这一任务,它也作为一个设计师,还是一个“提出者”,格式化数据的视图,它可以以一种有意义的方式显示。
回顾我们以前的错误例子,演示者可能会以一个错误对象的形式收到错误。而不是直接从错误的角度出发,它决定了恰当的信息来描述错误并将其传递给视图。
###处理事件
演示者通常会收到事件类型的方法调用两个来源:视图和交互。
##查看事件
由视图和部分的工作是通知的事件的报告,相应地处理这些事件。这通常意味着要求关联检索一些信息或执行某些任务。
在我们的日志中,该演示者将被通知的视图,试图事件已发生与特定的用户名和密码。提出者会问的关联进行尝试通过它调用适当的方法:
你可能猜到现在提出者在做什么;什么都没有,它的责任时事件的处理并且,它现在到了关联进行任务。
###Interactor events
###关联事件
提出者也可以从关联接受事件。这将发生在交互执行完成任务和用户应该知道结果。
例如在尝试登陆失败,交际者会告诉提出者:
提出者会把这个错误,把它转换成一个有意义的字符串,然后告诉使用该字符串显示一个错误。
#交互器
交互器的责任:
- 执行业务逻辑
关联是什么执行的应用程序,都是围绕着数据的业务逻辑。
###Performing business logic
###执行业务逻辑
关联是一个知道如何开展,查看通知提出者的事件。
例如,假设在应用程序中,需要向后端应用程序的同步网络请求。那是,请求B不能执行请求,请求B直到完成需要的信息,是在响应请求这会开始从提出者到关联的电话:
有一件事我们必须提出的是这里的关联本身并不直接处理网络请求。事实上,它甚至不知道网络请求正在发生。它所知道的是,它可以从数据管理器中的实体的形式(我们将详细说明这个概念很快)。记住,交际者的相应的方法调用上会是这个样子:
在这里,我们看到,关联调用的数据管理的必要方法,然后调用返回结果的提出者。这里的关键是交际者知道performmytask
需要 fetchfoowithcallback:
和 fetchbarwithfoo:回调:
被称为, fetchbarwithfoo:回调:
必须在回调块fetchfoowithcallback
。这样,关联处理的“业务逻辑”的应用。
#数据管理器
数据管理职责:
-
检索数据
-
存储数据(可选)
数据管理器是一个知道在哪里检索数据的人,如果它应该坚持或不。
#Retrieving data
#检索数据
我们看到在关联的例子,我们应该能够查询数据的数据管理,就得到正确的实体回来。我们不关心数据来自何处,因为数据管理器处理。
数据管理器清楚地知道在哪里检索特定的数据或执行某些请求。例如,关联可以从数据管理器请求用户对象:
如果这是一个应用程序的后端服务器的支持,数据管理器会知道它必须最终使网络请求检索用户数据。如果这是一个应用程序没有后台服务器的支持,数据管理器可以从本地持久存储中检索用户数据。对于网络请求,我们喜欢有数据管理查询服务对象:
我们很快就要进入服务对象,但这里的关键是,数据管理器知道要使用什么服务来检索特定的信息。一旦接收到信息,它的信息反馈给团员的继电器。
###Storing data
###存储数据
持续的数据也是数据管理者关注的问题。它知道什么时候它应该存储数据,如数据从服务器端,它可以存储缓存的目的。这里的主要原因是,没有其他类知道任何数据是持久的,因为数据管理器摘要。一个简单的例子,这是一个修改上面的代码:
让我们打破这个:
-
数据管理器首先检查是否已缓存版本的用户给出了相应的
userid
。 -
如果用户发现,关联的通知和方法返回。
-
如果未找到用户,则数据管理器必须从后端服务器检索用户,并使用一个服务来做到这一。
-
一旦用户从服务器检索,用户首先是持久的,那么关联的通知。
坚持数据是一个巨大的话题,所有的,可以实现许多方式。这个例子只是作为一个简单的教学模式,但这个想法将在这一切的背后,一个数据管理是一种VIPER的点。
#服务
服务职责:
- 向特定实体执行网络请求
服务对象的VIPER是没有必要的,但是我们已经发现非常有用。
向特定实体执行网络请求
我们已经找到服务是一个很好的方式来保持代码与网络请求干燥。其思想是一个单一的服务只处理一个实体类型,并且知道需要对该实体类型进行各种修改的网络请求。
例如,您可能有一个用户实体的服务。这个类的头文件可能看起来如下:
此服务知道如何创建用户、登录用户、获取用户对象。对于一个给定的身份证的用户对象的一些类似的东西,是一种很可能被用于在多个地方的应用程序,这是服务对象是方便的,在保持事情干。
#实体
实体职责:
- 代表数据
实体是非常直接的向前和你所期望的。它们体现了某种类型的数据,并作为一种“负载”,通过周围的其他类。例如,数据管理器返回一个实体的交互,它返回一个实体的提出者,然后使用该实体告诉视图显示什么信息。
#路由器/线框
线框职责:
-
初始化所有其他类
-
处理路由到其他视图的应用程序
路由器/线框是什么胶水,其他所有的VIPER组件彼此并处理从一个视图导航到另一个应用程序。
###初始化所有其他课程
你可能会想知道所有这些VIPER类被实例化和有线互相交谈。这是路由器/线框的地方来。
在VIPER,每一“堆”由一个观点,提出者,交互,数据管理和服务(我们无论如何),和实体。这一“堆”是什么VIPER是指作为一个模块。VIPER模块对应一个用例。一个用例是你的应用程序应该为用户执行的功能。
例如,一个用于许多应用程序共同使用的案例是允许用户登录帐户。为此,我们将有一个VIPER模块专门为“登录”的应用程序屏幕。这个模块将有:
-
显示“登录”屏幕。
-
一个演示者,处理事件,如用户要求登录在一个特定的用户名和密码。
-
一个团员知道叫上喜欢尝试登录用户在此类事件中的数据管理方法。
-
一个知道用以检索或发送信息到服务器的数据管理器。
-
服务知道HTTP URL请求。
-
实体,您的服务器响应被转换成这样的信息是有用的,在您的应用程序。
你会注意到,看来,提出者,关联,和数据管理是这个模块非常具体的。也就是说,他们只知道如何处理与日志记录有关的事情。另一方面的服务和实体是非常一般的东西,可以用在许多不同的模块。在这种情况下,该服务可能是一个知道如何将所有端点与服务器上的用户关联的服务。这可能包括登录、注册,或只是检索一般用户数据。你可能在这里使用的是一个用户实体,它只代表用户对象。很容易看到,这个实体可以在许多地方使用的应用程序。
现在我们已经解释了什么是VIPER“栈”或模块,我们可以解释的线框的部分责任。线框图是什么实例化实例这些VIPER组件和连接他们互相交谈。那是,它给出了视图和另一个提出者参考,提出者和彼此关联的引用,等等。实例化一个线框相当于实例化整个VIPER模块。
###处理路由到其他视图的应用程序
他线框的别名,路由器,是什么使得在VIPER的R。线框图知道如何浏览和其他模块时要求。这意味着线框也会有其他的框架参考。
例如,假设你有一个初始屏幕在你的应用程序,这是你的典型的家庭屏幕的应用程序,需要用户帐户。这个屏幕上有一个按钮,“注册”和“登录”按钮。这个屏幕是一个模块,让我们称之为注册提示(命名是硬的)。这里使用的情况是,用户应该能够看到他们的选项之前登录到您的应用程序。当用户按“登录”按钮时,典型的流程是:
-
视图将通知用户已请求登录的提示。
-
主持人会意识到这需要一个单独的模块,它将通知框。
现在,注册框线框将实例化的日志,从而实例化整个VIPER模块,和现在的日志视图在登记的观点(也许作为一个模态)。
#VIPER的利益
使用后的VIPER,我们发现它在许多方面是非常有益的。让我们回到我们出发去完成构建我们的应用程序时看看它们的地址列表中的VIPER。
-
易于迭代
-
友好合作
-
分离关注
-
规格能力
###易于迭代
一件事是非常有用的,在添加功能的应用程序是知道新的代码应该生活在旧的代码。VIPER与每个组件有一个明确的责任,不排成一个很好的工作,这使得它很容易决定,把新的代码。
我经常发现当我在一个模块中添加新功能时,它几乎感觉像是一个日常的例程,因为我已经知道每个代码应该放在哪里了,这只是一个完成的问题。
也就是说,我们遇到的情况,我们确定VIPER在放置一块代码,我们必须作出判断。例如,如果用户可以执行所有行动前选择列表中的多个项目,我们应该在视图,保持这个状态的主持人,或其他类的VIPER堆栈?一旦你弄清楚什么对你最有意义,你可以绕过这些问题,在未来很容易,因为你已经解决了他们。然后一切又变得例行。
###友好合作
VIPER在团队非常容易的工作。由于用例被分成不同的模块,你不踩别人的脚趾,因为所有的代码的一个特点是通常划分为自己的模块。
在实践中,每一个VIPER组件之间的交互通过接口。在Objective-C中这只是一个协议。很好的一点是,你可以定义一个类的接口,然后单独的人可以单独对这些类进行单独的工作。我们已经取得了很大成功,这被定义的视图演示界面提前对视图做纯粹的UI的工作一个人工作,一个人工作,其余的VIPER栈做纯粹的“后台”工作。
###分离关注
每个VIPER模块遵循单一责任原则,VIPER自然分离出来的关注类。这是什么使前两点(和最后一个点)工作了这么好。
###易于测试
通过分离元件,遵循单一责任原则,这也使事情容易说明它给你而掐灭其他依赖规格特异功能的能力。例如,如果你想测试你的交互逻辑,你所要做的就是找出提出者和数据管理器来消除可以使写作规格复杂的依赖关系。那么你的规格只会测试的关联,你可以写为提出者和数据管理单独的规格。
#结论
整体上我们发现使切换VIPER为上述原因是非常有用的,对我们有益。当然有许多的障碍,你会用VIPER需要模具VIPER需要你的遭遇,但那是真的任何建筑的你选择去。
我肯定会尝试VIPER推荐别人的同时也要在其他博客上看它。希望这个帖子一直帮助和得到一些你兴奋地尝试VIPER。
附加读数: