Cocoa MVC:“模型控制器”和“视图控制器”之间的交互

时间:2013-01-10 16:44:55

标签: macos cocoa model-view-controller

刚开始在BNR的 Cocoa Programming for Mac OS X(第4版)的帮助下学习Objective-C和Cocoa,我正在研究基于文档的应用程序。我已经阅读了Apple关于文档体系结构的开发人员文档,并选择了NSWindowController的子类并覆盖makeWindowControllers子类上的NSDocument。我有几个理由这样做:

  • 将模型逻辑(在NSDocument子类中)与视图逻辑分开(在NSWindowController子类中)。
  • 自定义我的文档窗口的标题(Apple的开发人员文档说,没有不必要的副作用的正确方法是子类NSWindowController并覆盖windowTitleForDocumentDisplayName:
  • Apple的文档似乎强烈建议为除了最简单的应用程序之外的所有应用程序继承NSWindowController,而我的文档绝对不是“简单”

因此,我的NSDocument子类是模型控制器,而我的NSWindowController子类是视图控制器。此外,我理解应用程序的“工作”大部分是在控制器对象中完成的,因为视图和模型应该尽可能与应用程序无关并且可以重用。现在我的问题是:这两种类型的控制器如何相互作用才真正实现“工作”?

例如,假设我正在编写电子表格应用程序,我想要一个菜单​​项(或工具栏按钮),它会显示一张表格,用于从我的某些数据创建图表或图表。在该表中,用户将输入有关如何创建图表或图表的各种参数和选项,然后单击“确定”(或调用该按钮)。

谁应该响应菜单项的操作,文档(模型控制器)或窗口控制器(视图控制器)?实际加载和显示工作表的任务似乎与“视图相关”,所以它应该进入窗口控制器,对吗?但是工作表的控制器需要一个模型显示给用户(Chart对象,或者ChartInputs);该模型在哪里创建并提供给纸张控制器?文档是否应该通过创建ChartInputs模型对象来响应菜单项,然后将其传递给窗口控制器,窗口控制器创建图纸控制器,将模型对象传递给它,并显示工作表?或者窗口控制器应该响应菜单项,请求新的模型对象(可能通过依赖注入通过某种工厂提供到窗口控制器的构造函数中),然后继续创建图纸控制器,传递模型,并显示表?

用户填写表单并单击“确定”后怎么办?应该在哪里返回控制来处理用户的选择并实际创建图表 - 窗口控制器,文档或两者?如果逻辑在用户单击“确定”之后但在表单被解除之前验证用户的输入(如果某些内容无效)?

1 个答案:

答案 0 :(得分:6)

首先,考虑NSDocument的无窗口操作。例如,您可以创建一个实用程序应用程序,它共享您的NSDocument类,打开脚本,打印或其他操作的文档,但不显示您的主文档窗口。想象一下,您的NSDocument类重用于该应用程序 - 并将您不想要的逻辑放入您的窗口控制器中。这样,NSDocument子类主要负责影响文档状态的活动。

这些是模型控制器(NSDocument子类)的职责:

  • 序列化和反序列化
  • 正在加载和保存
  • 操纵文档的状态
  • 管理和发送打印视图
  • 监控文档以供他人更改
  • 刷新支持数据源 - 影响文档模型的来源 - 并根据需要对模型和文档应用更改
  • 管理与文档相关的活动日志
  • 拥有撤消管理员
  • 将基本模型对象公开给视图控制器
  • 创建窗口控制器
  • 促进某些编辑行为,例如更改一个属性,触发创建或删除对象

如果您正在使用Core Data,那么您的托管对象上下文,持久性存储协调器和持久性存储是模型控制器的一部分,而不是模型。当然,托管对象本身就是其中的一部分该模型。

这将责任留给模型:

  • 用于插入,重新排列和删除模型对象的辅助方法
  • 访问模型特定部分的辅助方法
  • 数据验证
  • 以各种格式将模型渲染为字符串
  • 自行序列化和反序列化
  • 促进某些编辑行为,例如更改一个属性以触发对其他属性的更改

另一方面,这些是视图控制器的职责:

  • 操纵视图以使其与模型保持同步
  • 操纵视图以使其与文档状态保持同步
  • 响应本地化操作,例如添加或删除模型对象的按钮
  • 在这些视图中呈现辅助视图并响应输入
  • 调度受UI中选择影响的操作,例如表视图中的选定行
  • 管理编辑过程中使用的辅助控制器,与文档本身无关(如Web服务数据)
  • 用作视图对象的数据源

如果你正在使用Cocoa Bindings,你的绑定也是视图控制器的一部分。

这种设计在视图控制器和模型控制器之间产生了合理的职责分离。但是,它们两者位于视图和模型之间。虽然这会产生模块化,但它不会产生去耦。

虽然我确实考虑过无窗操作,但我很大程度上是凭经验得出这种设计模式 - 将相似的代码放在一起并分离出感觉不合适的代码。我很好奇,如果其他人发布权威来源或参考资料,对于如何做到这一点同意或不同意。


举个例子,我建议这个设计:

  1. EditorWindowController创建ChartParameters对象并为其提供对文档模型的引用:

    [[ChartParameters alloc] initWithWorkbook:self.document.workbook]
    
  2. EditorWindowController设置新图表视图,它可能有自己的NewChartViewController。它将ChartParameters和文档对象传递给NewChartViewController并显示窗口。

  3. ChartParameters对象负责验证用户的选择。 NewChartViewController需要操作视图以使其与验证结果保持同步。 (避免让用户犯错误:不要等到最后验证输入。)

  4. 视图完成后,NewChartViewController会要求模型使用给定的参数创建新图表:

    [self.document.workbook addChartWithParameters:self.chartParameters]
    
  5. 如果你想让尚未图表的对象成为你文档的一部分,你可以这样做:

    1. EditorWindowController要求文档模型创建一个新的图表对象:

      Chart *newChart = [self.document.workbook addChart]
      
    2. 新图表应设置一个标记,表示尚未准备好显示。

    3. EditorWindowController设置NewChartViewController,将其传递给图表,显示窗口。

    4. Chart对象验证用户的选择,NewChartViewController使视图保持同步。

    5. 完成后,告诉图表它已准备好显示。或者,如果用户取消,请将其删除。

    6. 在这些设计中的任何一个中,NewChartViewController都是一个模型控制器和视图控制器,针对其特定任务进行了本地化。