苹果眼中的MVC

2020-04-29  本文已影响0人  行知路

        基于苹果提供的库进行开发的同学应该都听说过MVC这种设计模式,也都实际中使用过这种模式,但是每个人对MVC的理解是不同的,按照自己的理解实现的MVC更是不同的。大家应该很想知道在苹果的眼中,他们所定义的MVC到底是什么样子的,以下文章翻译自苹果官方文档,让我们看看苹果对MVC的认知。

一、Model-View-Controller

        Model-View-Controller(MVC)这种设计模式具有悠久的历史。自Smalltalk诞生以来它就一直在演变(MVC的概念最早来自Smalltalk)。它是一种高阶的设计模式,主要考虑应用程序的全局架构,并根据对象在应用程序中所扮演的角色对其进行分类(划分为Model、View、Controller)。它是一个包含多种基本模式的复合模式。
        面向对象程序开发通过使用MVC模式可以获得多样的收益。这样的程序中的对象能够更好的被定义、重用。这些程序总体上更适应不断变化的需求——换句话说,与不基于MVC的程序相比,它们更容易扩展。此外,Cocoa中的许多技术和体系结构(例如绑定、文档体系结构等)都基于MVC,并且要求自定义对象能够扮演MVC中的某个角色。

二、MVC对象的角色与关系

        MVC设计模式认为存在三种类型的对象:模型对象,视图对象和控制器对象。MVC模式定义了这些类型的对象在应用程序中扮演的角色及其通信线路。 在设计应用程序时,主要步骤是选择或创建属于这三个组之一的对象。三种类型的对象中的每一种都通过抽象边界与其他类型的对象分开,并跨这些边界与其他类型的对象进行通信。

2.1 Model对象封装数据与基本行为

        模型对象代表特殊的知识和专长。他们保存应用程序的数据并定义处理该数据的逻辑。设计良好的MVC应用程序将其所有重要数据封装在模型对象中。一旦将数据加载到应用程序中,任何属于应用程序持久状态的数据(无论该持久状态存储在文件还是数据库中)都应驻留在模型对象中。因为它们代表与特定问题领域相关的知识和专长,所以它们具有可重用的特性。
        理想情况下,模型对象与用于呈现和编辑它的用户界面没有显式连接。例如,如果您有一个代表人的模型对象(例如您正在写通讯录),则可能要存储生日。将其存储在您的Person模型对象中是一件好事。但是,日期格式字符串或其他有关该日期显示方式的信息则存储在别处,更好一些。
        实际上,这种分离并不总是最好的,这里有一定的灵活性空间,但是通常,模型对象不应与接口和表示问题有关。一个合理的例外情况是一个绘图应用程序,该应用程序具有代表所显示图形的模型对象。图形对象知道如何绘制自己是有意义的,因为它们存在的主要原因是定义视觉对象。但是即使在这种情况下,图形对象也不应依赖于存在于特定视图或任何视图中,并且它们也不应该负责知道何时绘制自己。 应该要求它们通过要呈现它们的视图对象进行绘制。
        对于本段的所说的理论本人是赞同的,但是对于所举示例,本人则不赞同。在此示例中,更应该是由View对象来承担如何绘制的职责。在讲述设计模式、面向对象(讲继承的时候)等相关书籍、文章中,此示例经常被提及。

2.2 View对象向用户展示信息

        视图对象知道如何显示,并且可能允许用户编辑应用程序模型中的数据。 该视图不应负责存储其显示的数据。(当然,这并不意味着视图从不实际存储其显示的数据。出于性能原因,视图可以缓存数据或执行类似的操作)。 视图对象可以负责仅显示模型对象的一部分、整个模型对象甚至许多不同的模型对象。视图有很多不同的种类。
        视图对象倾向于可重用和可配置,并且它们在应用程序之间能够表现出一致性。在Cocoa中,AppKit框架定义了许多视图对象,并在Interface Builder库中提供了许多视图对象。通过重用AppKit的视图对象(例如NSButton对象),可以确保应用程序中的按钮的行为与其他任何Cocoa应用程序中的按钮一样,从而确保了各个应用程序在外观和行为上的高度一致性。
        视图应确保其正确显示模型。 因此,它通常需要了解模型对象发生了更改。由于模型对象不应绑定到特定的视图对象,因此它们需要一种通用的方式来通知它们已更改。

2.3 控制器对象把模型绑定到视图

        控制器对象充当应用程序的视图对象与其模型对象之间的中介。控制器通常负责确保视图可以访问他们需要显示的模型对象,并充当视图了解模型更改的渠道。控制器对象还可以为应用程序执行设置和协调任务,并管理其他对象的生命周期。

        在典型的Cocoa MVC设计中,当用户输入值或通过视图对象改变选择时,该值或选择将传达给控制器对象。控制器对象可能以某种特定于应用程序的方式解释用户输入,然后要么告诉模型对象如何处理此输入(例如,“添加新值”或“删除当前记录”)。

        基于相同的用户输入,某些控制器对象还可能告诉视图对象更改其外观或行为的某个方面,例如告诉按钮禁用自身。 相反,当模型对象发生更改时(例如,访问新的数据源),模型对象通常会将更改传达给控制器对象,然后控制器对象请求一个或多个视图对象进行相应的更新。

        控制器对象可以是可重用的也可以是不可重用的,这取决于它的类型。控制器类型小节讲述了cocoa中不同的控制器对象。

2.4 组合角色

        在MVC中,一个对象和合并其他对象的职责,例如,把控制器对象与视图对象的职责合并到一起,称之为ViewController。同样,也可以有一个ModelController对象。对于一些应用程序,组合角色是可以接受的设计

        模型控制器是主要与模型层有关的控制器。它“拥有”模型;它的主要职责是管理模型并与视图对象进行通信。适用于整个模型的动作方法通常在模型控制器中实现。文档体系结构为您提供了许多这些方法。例如,一个NSDocument对象(它是文档体系结构的中心部分)自动处理与保存文件有关的操作方法。

        视图控制器是主要与视图层相关的控制器。它“拥有”界面(视图);它的主要职责是管理界面并与模型进行通信。与视图中显示的数据有关的动作方法通常在视图控制器中实现。NSWindowController对象(也是文档体系结构的一部分)是视图控制器的示例。

三、Cocoa控制器类型

        控制器对象把模型绑定到视图概述了控制器对象的抽象概念。但实际上,情况要复杂得多。 在Cocoa中,有两种通用的控制器对象:中介控制器和协调控制器。每种控制器对象都与一组不同的类相关联,并且每种提供不同的行为范围。

        中介控制器通常是从NSController类继承的对象。中介控制器对象用于Cocoa绑定技术中。它们促进或调解了视图对象和模型对象之间的数据流。

在iOS中,不可使用此类型的控制器对象。

        中介控制器通常是您从Interface Builder库中拖动的现成对象。您可以配置这些对象,以在视图对象的属性和控制器对象的属性之间,然后在那些控制器属性和模型对象的特定属性之间建立绑定。结果,当用户更改在视图对象中显示的值时,新值将自动通过中介控制器传递给模型对象进行存储。 当模型的属性更改其值时,该更改将传达给视图以进行显示。抽象的NSController类及其具体子类(NSObjectController,NSArrayController,NSUserDefaultsController和NSTreeController)提供了支持功能,例如提交和放弃更改的能力以及对选择和占位符值的管理。

        协调控制器通常是NSWindowController或NSDocumentController对象(仅在AppKit中可用),或者是NSObject的自定义子类的实例。 它在应用程序中的作用是监视或协调整个应用程序或部分应用程序的功能,例如从nib文件中未归档的对象。 协调控制器提供以下服务:

        NSWindowController和NSDocumentController是基于文档的应用程序的Cocoa体系结构的一部分。这些类的实例为上面列出的几种服务提供了默认实现,您可以创建它们的子类来实现更多特定于应用程序的行为。您甚至可以使用NSWindowController对象来管理不基于文档体系结构的应用程序中的窗口。

        协调控制器通常拥有归档在nib文件中的对象。作为文件的所有者,协调控制器位于nib文件中的对象外部,并管理这些对象。这些拥有的对象包括中介控制器以及窗口对象和视图对象。有关将控制器协调为文件所有者的更多信息,请参见MVC作为复合设计模式。

        自定义NSObject子类的实例可以完全适合用作协调控制器。这些类型的控制器对象结合了中介功能和协调功能。对于中介行为,他们利用诸如目标动作,出口,委托和通知之类的机制来促进数据在视图对象和模型对象之间的移动。它们往往包含许多粘合代码,并且由于该代码专门针对特定应用程序,因此它们是应用程序中最少可重用的对象。

四、作为复合模式的MVC

        Model-View-Controller是一种设计模式,由多个其他基本设计模式组成。这些基本模式共同定义MVC应用程序所特有的功能和通信路径。但是,MVC的传统概念与Cocoa的不同在于——Cocoa赋予控制器和视图对象的角色不同。

        在Smalltalk中,MVC由策略、组合、观察者模式组合而成。

        下图描述了Composite,Strategy和Observer模式协同工作的传统方式:用户在Composite结构的某个级别上操作视图,生成了一个事件。 控制器对象接收事件并以特定于应用程序的方式解释该事件,即,它应用了策略。该策略可以是请求(通过消息)模型对象以更改其状态,或请求视图对象(在复合结构的某个级别)以更改其行为或外观。当状态改变时,模型对象又通知所有已注册为观察者的对象。 如果观察者是视图对象,则可以相应地更新其外观。

传统MVC

        MVC的Cocoa版本作为复合模式与传统版本有些相似,实际上,根据上图构建可运行的应用程序是完全可能的。通过使用绑定技术,您可以轻松创建一个Cocoa MVC应用程序,该应用程序的视图直接观察模型对象以接收状态更改的通知。但是,这种设计存在理论上的问题。视图对象和模型对象应该是应用程序中最可重用的对象。视图对象表示操作系统和系统支持的应用程序的“外观”。外观和行为的一致性至关重要,并且需要高度可重复使用的对象。根据定义,模型对象封装与问题域关联的数据并对该数据执行操作。在设计方面,最好使模型和视图对象彼此分开,因为这样可以增强它们的可重用性。

        在大多数Cocoa应用程序中,模型对象中状态更改的通知通过控制器对象传递给查看对象。下图显示了这种不同的配置,尽管涉及了两个以上的基本设计模式,但看起来更清晰。

CocoaMVC

        此复合设计模式中的控制器对象包含中介者模式和策略模式; 它在两个方向上中介模型和视图对象之间的数据流。 通过应用程序的控制器对象传达模型状态的更改以查看对象。此外,视图对象通过其对目标动作机制的实现来合并命令模式。

目标动作机制使视图对象能够传达用户输入和选择,可以在协调和中介控制器对象中实现。但是,每种控制器类型的机制设计都不同。 对于协调控制器,可以在Interface Builder中将视图对象连接到其目标(控制器对象),并指定必须符合特定签名的操作选择器。由于是窗口和全局应用程序对象的委托,因此协调控制器也可以位于响应者链中。中介控制器使用的绑定机制还将视图对象连接到目标,并允许使用可变数量的任意类型的参数进行动作签名。 但是,中介控制器不在响应者链中。

        上图描述的修订的复合设计模式,在现实上和理论上都有根据,尤其是在中介者设计模式方面。中介控制器从NSController的具体子类派生而来,这些类除了实现Mediator模式外,还提供了应用程序应利用的许多功能,例如选择和占位符值的管理。 而且,如果您选择不使用绑定技术,则您的视图对象可以使用诸如Cocoa通知中心之类的机制来接收来自模型对象的通知。 但这将需要您创建一个自定义视图子类,以添加有关模型对象发布的通知的知识。

        在设计良好的Cocoa MVC应用程序中,协调控制器对象通常拥有中介控制器,这些控制器存储在nib文件中。 下图显示了两种类型的控制器对象之间的关系。

中介控控制器与协调者控制器

五、MVC应用程序设计指南

        以下准则适用于基于模型-视图-控制器的应用程序设计:

六、Model-View-Controller in Cocoa (OS X)

模型-视图-控制器设计模式是许多Cocoa机制和技术的基础。因此,在面向对象的设计中使用MVC的重要性不仅限于为自己的应用程序获得更大的可重用性和可扩展性。如果您的应用程序要结合基于MVC的Cocoa技术,那么,如果其设计也遵循MVC模式,则你的应用程序将运行最佳。如果你的应用程序具有良好的MVC分隔,那么使用这些技术应该相对容易一些,但是如果你没有很好的分隔,那么使用这种技术将花费更多的精力。

OS X中的Cocoa包含以下基于模型-视图-控制器的体系结构,机制和技术:

上一篇 下一篇

猜你喜欢

热点阅读