在MVC中,模型对其环境是盲目的,视图也可以是这样的——将其事件(盲目地)传递给控制器,后者更了解视图和模型。因此,归根结底,控制器是系统中“不可重用”的一部分,因为它是最具上下文感知能力的组件。
如果我以某种方式更改了影响控制器的模型...
那么模型应该以一种简单的CRUD方法公开,以使使用这些方法的人不必知道传递的更新对象的任何信息,也不必知道模型内部实际发生了什么。
这意味着视图需要做一些工作,通过创建传递的记录,因为控制器应该是无状态的,而视图更加持久化。控制器会触发并“启动”,使用传递的对象进行工作,并且没有状态。
传递的数据是通过某种通用约定创建的。
让我再进一步。假设您有一个视图、一个表格和一个控件,其中控件的启用属性取决于在网格中选择的项目——您可以创建一个处理这两个控件和这个逻辑的视图,这可能是这样一个简化示例的方法。
但是,您的视图越原子化,它们就越可重用,因此您为每个控件创建一个视图。现在,您正在查看一种情况,其中视图必须知道彼此才能为正确的通知注册自己...
这就是控制器发挥作用的地方,因为我们希望将所有这些依赖关系都放在长期可丢弃的控制器上。因此,控制器管理此类型的视图到视图的通知方案。
现在,您的视图可以无知,并且是独立的,因此可重用。
您可以编写视图而无需了解系统或所谓的“业务逻辑”。您可以编写模型而无需过多了解您的目标(尽管调整模型以使其能够返回您所考虑的数据集有所帮助)...但是,控制器是最后一个,您必须先确定前两个,然后才能将它们连接起来。
这里还有一件事要考虑——正如模型应该抽象出并提供对其管理的数据底层实现的通用接口一样(客户端不知道数据来自数据库、文件、程序设置等),视图也应该抽象出其使用的控件。
因此,最终这意味着视图不应该(下面有一个警告)具有以下函数/属性:
public property BackgroundColor{get;set}
Nor
public function ScrollBy(x,y){}
但实际上:
public SetProp(string name, object val){}
并且
public DoCmd(string name, object val){}
这有点牵强,但请记住我说过最终目标是……你会问这为什么是一个好主意?
考虑到可重用性,想象一下,也许有一天你想从WinForms移植东西到Flex,或者只是想使用一个新颖的控件库,它可能不会暴露相同的功能。
我在这里说“移植”,但这真的不是目标,我们不关心移植这个特定的应用程序,而是使底层MVC元素足够通用,以便可以跨越到一个新的风格——内部保持一致和能力独立的外部接口不变。
如果你不这样做,那么当你的新风格出现时,所有对视图属性的硬引用(可能是可重用/可重构/可扩展的)在控制器中都必须被修改。
这并不意味着这样的通用设置器和命令必须成为所有视图能力的接口,而是它们应该处理“边缘情况”属性以及您可以以传统的硬链接方式公开的常规属性/命令。把它看作是一个“扩展属性”处理程序。
这样,(再次牵强),假设你正在构建一个框架,其中你的按钮不再具有buttonIcon属性。这很酷,因为你有预见性地创建了一个按钮视图接口,其中buttonIcon是一个扩展属性,在视图内部,当它接收到set/get时,你的条件代码现在会执行空操作。
总之,我想说的是MVC的编码目标应该是为其底层组件提供通用接口,使得在编写控制器时,你不必过多考虑你正在控制谁。虽然控制器似乎被设置成长期可重用性的牺牲品,但这并不意味着所有的控制器都注定要消亡。
它们希望是小的,因为他们的大量“思考”已经转移到了半智能模型和视图以及其他控制器(例如:控制器对网格进行排序或操作TreeView)——所以它们很小,可以很容易地查看和确定在你的下一个项目中是否可以重用,或者克隆和调整以变得合适。