iOS设计模式-第1部分(MVC,MVP,MVVM)

设计模式始终有助于构建可管理,可测试,可重用和优化的软件。 通常,它有助于对软件进行模块化,以使每个组件都是独立的,并承担单个责任。 此外,它们极大地提高了代码的可读性,这在传达软件代码中起着重要作用。 而且,软件开发过程可以大大加快采用已经证明的设计范例的速度。 移动开发人员无法摆脱遵循设计模式所带来的好处。 最初,移动应用程序太小,无法遵循设计或架构模式,因此它们过去一直严格遵循最基本的应用程序。 如今,由于移动应用程序越来越大,几乎可以反映它们的桌面或Web对应部分(从功能上来说),因此它们必须在实际进入开发模式之前考虑设计模式。

最近,我一直亲自或通过网络参加iOS和Swift开发者大会。 每隔一个这样的会议,至少有两到三个关于新兴或公认的设计模式的演讲,尤其是考虑到移动应用程序。 像iOS这样的移动平台已经建议开发人员在其应用程序中遵循MVC(模型-视图-控制器)。 苹果的MVC是原始MVC的修改版,可以很好地与移动应用程序配合使用,但是开发人员社区对此并不满意。 因此,开发人员社区一直在尝试其他软件开发平台中已经实践过的不同设计模式。 我们将看到这样的设计模式被社区广泛接受,并且过渡也相当成功。 像所有其他设计模式一样,这些模式也不适合所有情况。 每个案例都有其优点和缺点,因此,明智地选择案例中的一个是我们的全部责任。 如果没有适合您的工作,开发人员可以根据您的需要帮助您生成新的工作。

我们将介绍iOS开发人员社区中一些最受欢迎的工具,MVC,MVP,MVVM和VIPER。 这篇文章将分为两部分。

MVC(模型-视图-控制器)

让我们从最简单,最常用的一个开始。 苹果始终通过其示例代码始终建议遵循MVC。 如前所述,对Apple形式的MVC进行了一些修改,以更好地适应移动应用程序。 它的一般形式表示如下:

它基本上由一个视图,一个控制器和一个模型组成。 可以将视图想象为某个特定时间点向用户显示的用户界面。 模型是要在视图的组件中显示的数据。 控制器是它们之间的桥梁。 这三个,总是保持一种关系。 控制器拥有一个视图,并将一个模型与之关联。

该视图负责向用户显示其用户界面。 最终,视图将源自用户界面的动作传递给Controller。 动作是用户启动的,例如,用户可以点击屏幕上存在的按钮以启动动作,或者有时是自动生成的,例如,屏幕可能需要及时刷新其内容。 控制器的责任主要是从View接收这些动作并对其采取行动。 控制器在处理时可以更新模型。 有时此更新过程需要一段时间。 模型在完成更新过程时会通知Controller,控制器随后会通知View,以使用更新的数据更新其用户界面。 视图和模型永远不会直接对话。 他们通过控制器进行通信。 与原始MVC相比,这是Apple MVC拥有的唯一区别,如下所示。

苹果公司可能已经考虑过不将View与Model紧密绑定,因为移动应用程序中的View可以被高度复用。 如果保持紧密的联系,我们将需要每次针对不同的模型重新实现视图。 与模型无关的视图是Apple MVC背后的实际想法。

很简单,对不对? 但是,MVC的这种简单性带来了一些问题和困难。 直到移动应用程序变得更短,更简单,才面临这些问题。 但是现在,当iOS设备的功能比台式机强大时,用户希望移动应用程序具有足够的功能来充分利用设备资源。 更多的期望导致更大的应用程序,该应用程序通常集成了多个组件,进而导致更大的代码库。 如果每个组件的职责有限,则可以很好地管理具有多个组件的较大代码库。

首先,MVC引起的问题是,它没有遵循单一责任范式,从而导致了MVC。 什么? MVC导致MVC? 是的,MVC(模型视图控制器)导致MVC(Massive View Controllers)。 根据MVC,控制器是视图和模型之间的中介。 这迫使控制器承担许多责任。 现代用户界面的设计方式是,单个屏幕可以提供应用程序的某些功能。 然后,单个屏幕(视图)由多个功能子视图组成,为此,屏幕的控制器需要与几个不同的复杂模块集成在一起。 管理多个功能子视图并与不同模块集成是控制器的责任。 在这种情况下,控制器将管理View的生命周期,处理用户操作,这些操作在基于客户端-服务器的应用程序中可能需要进行服务器调用,处理响应,处理错误,运行屏幕刷新计时器,处理多个回调,监听通知,处理视图的方向等等。 这使控制器的代码更大。 显然,代码变得非常难以管理和理解。 在iOS中引入了子控制器的概念后,此问题得到了轻微解决。

其次,MVC使测试单元测试用例变得困难。 由于Controller-View的直接关系,开发人员需要进行调整和调整,以使仅执行业务逻辑来生成有效的测试用例结果。

在许多情况下,MVC仍然可以很好地工作,在这些情况下,用户界面更简单,控制器需要处理的责任更少,但对于复杂的用户界面却失败了。

MVP(模型视图呈现器)

MVP只是Apple MVC的扩展,其中视图控制器的职责分布在View和Presenter之间。 确实将控制器视为视图。 视图保持UI处理职责,而Presenter处理实际的业务逻辑(更新模型等)。 这样,由于所有单元测试用例都写在无法进行与视图相关的处理的Presenter上,因此单元测试更加容易。 在某种程度上,它比Apple的MVC更具优势,它可以很好地分配职责,并且测试单元测试用例的工作不会那么繁琐。 尽管实现了Presenter并将其绑定到各个层会带来一些额外的工作,但是它降低了开发速度。

MVVM(模型-视图-视图模型)

这是在现代应用程序中崭露头角的最新模式。 它最初是由Microsoft在2005年引入的,它使事件驱动的编程更加容易。 移动开发者社区发现它也对移动应用程序有益。 它被证明适合MVC可能引起的大多数挑战。

MVVM要求将责任分配给View(视图/视图控制器),模型和View模型。 像MVP一样,MVVM也将视图控制器视为视图的一部分。 我们已经从MVP模式中了解了视图和模型的职责。 ViewModel是此处的新组件,负责与UI无关的处理。 在责任分担方面,似乎是MVP中的演示者。 在MVP中,视图特定的数据创建逻辑与视图保持一致。 演示者只是充当视图与模型之间的中介者,并与视图控制器共同承担一些责任。 由于分配不当,视图控制器在MVP中仍然需要处理很多责任。 但是,在MVVM中,正如其名称所暗示的,视图模型是模型的视图特定表示。 View拥有一个视图模型,而该视图模型又拥有一个模型。 因此,MVVM中的典型流程是,视图从服务器或数据库获取数据,然后将其提供给视图模型。 然后,视图模型对其进行处理,并通过绑定进行通知。 视图模型的数据处理的一个小示例可能是,一个模型可能具有以毫秒为单位的时间戳,但是视图可能希望以用户友好的方式表示该时间戳。 在这种情况下,视图模型将负责将timestamp(millis)转换为格式化的日期字符串。 然后,视图使用此格式化的日期字符串显示给用户。 视图模型本身保持了表示性数据创建的复杂性,因此减少了视图(控制器)的责任。

视图模型通过绑定与视图共享更新。 如果我们谈论在iOS中实现绑定,则没有原生方法。 我们仍然可以通过KVO,委托或通知来实现类似的机制。 这不能像其他编程语言中的绑定那样有效地工作。 为了有效地实现绑定,开发人员已开始使用反应性方法(比通过KVO或通知实现绑定的更好方法)链接MVVM。 MVVM有助于分配职责,而响应式方法有助于将视图模型与视图绑定。

尽管视图模型与视图紧密结合,但我们设法将视图和模型分开,这是Apple MVC的强制要求。 此外,业务逻辑(由视图模型处理)与视图控制器的分离使单元测试变得容易。

MVVM确实有其自身的挑战。 它对职责分配进行了非常高层次的解释。 由于它不是起源于牢记移动应用程序的,因此移动开发人员在向谁进行欺骗方面制造了很多东西。 其中一项辩论将讨论与网络相关的代码在哪里? 要查看还是要查看模型? 原始MVVM表示它应该属于视图。 在我看来,这是有道理的,因为错误场景可以由视图直接处理,这可以使视图向用户显示错误或再次尝试执行操作。 如果View模型想要保留网络代码以使其从视图中抽象出来,则它需要使用错误对视图进行响应,在这种情况下,将添加额外的抽象层。 最后,开发人员可以根据自己的需要进行控制,以决定和分配职责。

开发人员面临的另一个挑战是反应式方法非常复杂。 它确实带有学习曲线,并且容易出现错误,开发人员可能会发现这些错误甚至很难发现问题。

从理论上讲,这似乎是一个很好的架构模式,并且在实际情况下也能很好地工作。 对于较简单的用户界面,这不是最佳选择。

希望这篇文章对您有所帮助。 在本博文的最后一部分,我将介绍VIPER设计模式。