我有一个使用MVC的应用程序,但是我在如何设计控制器方面有些挣扎。例如,视图仅一次查看模型数据的某些子集。但是,我不确定应该如何安排。例如,视图或模型直接在Controller上调用函数是否正常?通过某种界面?还是它们被完全封装而从不了解Controller或彼此?
就像编辑一样;这是一个不在任何Web框架中编写的自定义应用程序,因此我不在这里查找特定于框架的详细信息,并且可以自由选择。
我有一个使用MVC的应用程序,但是我在如何设计控制器方面有些挣扎。例如,视图仅一次查看模型数据的某些子集。但是,我不确定应该如何安排。例如,视图或模型直接在Controller上调用函数是否正常?通过某种界面?还是它们被完全封装而从不了解Controller或彼此?
就像编辑一样;这是一个不在任何Web框架中编写的自定义应用程序,因此我不在这里查找特定于框架的详细信息,并且可以自由选择。
Answers:
控制器控制活动流程。用户执行此操作,控制器将视图数据传递到域,该域将执行其所需做的一切,然后根据响应,控制器告诉框架接下来要显示哪个视图(并为其提供足够的数据所以)。
因此,控制器必须在某种程度上耦合到域模型。即。您可以在两者之间放置一个服务层,但严格来说,它成为域的一部分。
它还耦合到视图数据,但不耦合到视图本身。即。它只是说“使用此客户详细信息显示客户视图”。然后,框架决定应该在哪里找到该视图。
现在,这应该允许您通过使用相同数据的视图模型将域模型与视图分离。有些开发人员这样做,有些则不这样做,我认为这很大程度上取决于个人喜好。
在Rails中,非常鼓励您将域对象(ActiveRecord)推送到视图,并相信该视图不会利用该访问权限(例如,即使从视图中调用,也不应该调用customer.save)将可用)。
在.NET世界中,我们倾向于通过不允许不应该发生的事情来降低风险,并且可能由于这个原因,在我看来,分离视图模型更受欢迎。
注意:罗伯特·C·马丁(Robert C. Martin,又名鲍勃叔叔)在他的主题演讲《迷失的年代》中以一种更好和幽默的方式对此进行了解释。有点长,但是教了很多好的概念。
tl; dr:不要从MVC角度考虑和规划您的应用程序。MVC框架只是实现细节。
关于MVC的最令人困惑的事情是,开发人员试图使用所有粘合在一起的组件。
尝试根据程序而非框架进行思考。
您的程序有目的。它需要一些数据,对数据进行处理,然后返回一些数据。
这样,这controller
就是程序的交付机制。
$user->addToCart($product)
addToCart
为user
对象的功能)完成了预期的工作并返回响应(假设success
)view
:在控制器对象中$this->render($cartView('success')
这样,控制器便与程序脱钩,并用作传送机制。他们不知道您的程序如何工作,只知道需要为请求调用程序的哪一部分。
如果要使用其他框架,则无需更改应用程序,只需编写相关的控制器即可调用程序以获取请求。
或者,如果您要制作桌面版本,则您的应用程序将保持不变,您只需要准备一个交付机制。
和Model
。将其视为持久性机制。
以面向对象的方式,程序中有保存数据的对象。
class User {
//...
private $id;
private $shoppingCart;
//...
}
class Product {
//...
private $id;
//...
}
将产品添加到购物车时,可以将添加product::id
到user::shoppingCart
。
而且,当您要保留数据时,可以使用model
框架的一部分(通常使用ORM组成)将类映射到数据库表。
如果要更改使用的ORM,您的程序将保持不变,仅映射信息会更改。或者,如果您想避免一起使用数据库,则可以将数据写入纯文本文件,而您的应用程序将保持不变。
因此,请首先编写程序。如果以“ OO”方式编程,请使用该语言的普通旧对象。首先不要考虑MVC。
MVC
是的方式写的。这就是我写信的原因MVC Framework
。
Martin Fowler在描述MVC范例方面做得很好。这是他的文章链接,网址为http://martinfowler.com/eaaDev/uiArchs.html
请注意他对分隔表示的引用:“分隔表示的思想是在建模我们对现实世界的感知的领域对象与作为屏幕上看到的GUI元素的显示对象之间进行清晰的划分。”
这是一个如何在典型的Java Swing应用程序中使用MVC的简单示例...
假设您有一个包含Button和TextField的Panel。当按下按钮时,将触发一个事件,导致应用程序中的某些状态更改。注册状态更改后,TextField将被禁用。
那么,这将是简单的MVC应用程序采用的典型方法。
控制器将自己注册为View事件的侦听器。单击“按钮”后,视图本身将不处理事件。控制器会这样做。控制器特定于Swing,因为它必须处理与Swing相关的事件。
控制器收到此通知,必须决定由谁来处理(视图或模型)。由于此事件将更改应用程序的状态,因此它决定将信息转发给负责数据和程序逻辑的模型。有些人犯了将程序逻辑放入控制器的错误,但是在OOP中,模型既代表数据又代表行为。阅读Martin Fowler对此的看法。
该消息在适当的上下文中由模型接收。也就是说,它完全没有对Swing的任何引用或任何其他GUI特定的引用。此消息仅对模型说话。如果您发现自己在模型中导入javax.swing语句,则说明您未正确编码模型。
然后,该模型将其状态设置为“已禁用”,并继续将此模型更改通知任何相关方。对这个事件感兴趣的View已经将自己注册为任何模型更改的观察者。View收到Model状态更改事件后,将继续禁用其TextField。View也可以直接从其模型获取只读信息,而不必通过Controller(通常通过Model公开的用于此类活动的特定接口),这也是合法的
通过促进表示层,业务逻辑层和数据层之间的这种松散耦合,您将发现代码更易于维护。随着系统的发展,您使用MVC的方法也将随之发展。例如,分层MVC是一个扩展,通常用于将MVC三元组链接在一起以形成大型企业级系统,而无需将子系统耦合在一起
耦合(您要避免的排序)涉及两个类之间的相互依赖性。也就是说,Foo依赖于Bar,而Bar依赖于Foo,因此您不能真正修改一个而不修改另一个。那是一件坏事。
但是,您无法避免具有某些依赖关系。班级必须彼此了解一点,否则他们将永远无法交流。
在MVC模式中,控制器控制域模型和演示视图之间的通信。因此,控制器必须对模型有足够的了解,以要求其执行应做的事情。控制器还必须对视图有足够的了解,以便能够将其呈现给客户端或用户。因此,模型控制器对这两者都有依赖性。但是,如果没有Controller,则View可以完美存在-那里没有依赖性。同样,模型在控制器上也没有依赖关系-就是事实。最后,模型和视图完全相互独立。
本质上,控制器是将视图与模型分离的间接级别,因此它们不必彼此了解。
根据我的经验,一般的模型只依赖于一个视图,而不是具体的一个,经常作为观察员...如果它在所有此类耦合。
该视图通常会与所查看的内容耦合,这是有道理的。很难提出可以与视图分离的视图...但是有时您可能会有部分耦合或其他东西。
控制器通常倾向于同时耦合到两者。这也是有意义的,因为它的工作是将视图事件转换为模型更改。
当然,这只是我观察到的一种趋势,对于任何特定示例并没有真正说出任何话。
要了解MVC是什么以及耦合关系趋向于什么,您应该研究MVC的发展方式。创建MVC的环境是其中不存在“小部件”作为可用于构建对话框的表单元素的环境。一个“视图”是一个盒子,它吸引了东西。文本视图将是一个可以绘制文本的框。列表视图是一个可以绘制列表的框。“控制器”从该视图中收到来自UI系统的所有鼠标和键盘事件。没有“ textChanged”或“ selectionChanged”事件。控制器将处理所有这些低级别事件并生成与模型的交互。模型一旦更改,将通知其观点;从那以后,我们开始将这种关系视为“观察者”,并且
这就是MVC模式的本质。由于通常不再进行这种低级UI编程,因此MVC已朝许多不同的方向发展。如今,以该名称命名的某些东西几乎根本不像MVC那样,应该将其称为其他东西。尽管从整体上讲是与较大对象交互的对话框,但仍可以使用它。不过,还有许多更好的选择。
基本上,MVC想要解决的所有事情现在都发生在小部件内部,这不再是我们必须使用的东西。
对于那些认为他们更了解的人:
http://www.codeproject.com/Articles/42830/Model-View-Controller-Model-View-Presenter-and-Mod
http://msdn.microsoft.com/en-us/library/ff649643.aspx
我敢肯定还有更多,但这些只是Google列表的顶部。如您所见,该模型非常依赖于许多实现中的视图界面。通常,模型是可观察的,视图是观察者。
但是为什么要让事实成为障碍...
已经在另一个答案中发布的文章也支持我的陈述:
http://martinfowler.com/eaaDev/uiArchs.html
如果人们想继续说设计行业的每个人都是错误的,那很好。
如果控制器与视图紧密耦合,那么我们将处在Web表单的世界中。您将得到一个与模板文件绑定的代码(适用于ASP.NET Web表单)
因此,控制器未与模型或视图耦合。这只是一种处理请求和发送响应的机制。
视图与模型紧密耦合。更改模型(例如更改其属性),则必须更改视图。
模型未与视图紧密耦合。更改视图,它不会对模型产生影响。
模型对控制器或使用它的视图一无所知。因此,模型未与视图或控制器紧密耦合。
考虑这种情况的另一种方法:
更改控制器-视图和模型将不受影响
更改模型-视图将因为依赖模型而中断
更改视图-模型和控制器将不受影响
MVC项目中的这种松散耦合使它们易于进行单元测试。