首页 文章

什么进入“MVC”的“控制器”?

提问于
浏览
169

我想我理解MVC的基本概念 - 模型包含应用程序的数据和行为,View负责将其显示给用户,Controller负责处理用户输入 . 我不确定的是控制器中究竟是什么 .

让我们说例如我有一个相当简单的应用程序(我专门考虑Java,但我认为相同的原则适用于其他地方) . 我将我的代码组织成3个名为 app.modelapp.viewapp.controller 的包 .

app.model 包中,我有一些类反映了应用程序的实际行为 . 这些 extends Observable 并使用 setChanged()notifyObservers() 在适当时触发视图更新 .

app.view 包有一个类(或几个类用于不同类型的显示),它使用 javax.swing 组件来处理显示 . 其中一些组件需要反馈到模型中 . 如果我理解正确,View不应该与反馈有任何关系 - 应该由Controller处理 .

那么我实际上在Controller中放了什么?只需调用Controller中的方法,我就可以将 public void actionPerformed(ActionEvent e) 放在View中吗?如果是这样,是否应在控制器中进行任何验证?如果是这样,我如何将错误消息反馈回View - 如果再次通过Model,或者Controller是否应该直接将其发送回View?

如果在视图中完成验证,我应该在Controller中添加什么?

很抱歉这个问题很长,我只想记录我对这个过程的理解,希望有人可以为我澄清这个问题!

13 回答

  • 9

    在您建议的示例中,您说得对:“用户点击界面中的'删除此项'按钮'基本上只需调用控制器的”删除“功能 . 但是,控制器不知道视图的样子,因此您的视图必须收集一些信息,例如“单击了哪个项目?”

    在对话形式中:

    View :"Hey, controller, the user just told me he wants item 4 deleted."
    Controller :"Hmm, having checked his credentials, he is allowed to do that... Hey, model, I want you to get item 4 and do whatever you do to delete it."
    Model :"Item 4... got it. It's deleted. Back to you, Controller."
    Controller :"Here, I'll collect the new set of data. Back to you, view."
    View :"Cool, I'll show the new set to the user now."

    在该部分的末尾,您有一个选项:视图可以发出单独的请求,“给我最新的数据集”,因此更纯粹,或者控制器通过“删除”隐式返回新数据集“操作 .

  • 1

    MVC 的问题在于人们认为视图,控制器和模型必须尽可能彼此独立 . 它们没有 - 视图和控制器经常交织在一起 - 将其视为 M(VC) .

    控制器是用户界面的输入机制,通常在视图中被纠缠在一起,特别是对于GUI . 然而,视图是输出,控制器是输入 . 视图通常可以在没有相应控制器的情况下工作,但是如果没有视图,控制器通常就没用了 . 用户友好的控制器使用视图以更有意义,更直观的方式解释用户的输入 . 这就是它使控制器概念与视图难以分离的原因 .

    可以将密封盒中检测区域的无线电控制机器人视为模型 .

    该模型完全是关于状态和状态转换,没有输出(显示)的概念或触发状态转换的内容 . 我可以获得机器人在场地上的位置,机器人知道如何转换位置(向前/向后/向左/向前迈出一步 . 很容易想象没有视图或控制器,但没有任何用处

    想想没有控制器的视图,例如有人在另一个房间的网络上的另一个房间里看着机器人的位置为(x,y)坐标在滚动控制台上向下流动 . 这个视图只显示模型的状态,但是这个人没有控制器 . 同样,在没有控制器的情况下很容易想象这个视图 .

    想象一下没有视图的控制器,例如有人锁在壁橱里,无线电控制器调到机器人的频率 . 该控制器正在发送输入并导致状态转换,而不知道它们对模型做了什么(如果有的话) . 如果没有来自视图的某种反馈,很容易设想,但不是很有用 .

    大多数用户友好的UI使用控制器协调视图,以提供更直观的用户界面 . 例如,想象一个带触摸屏的视图/控制器,显示机器人在2-D中的当前位置,并允许用户触摸恰好位于屏幕前方的屏幕上的点 . 机器人 . 控制器需要有关视图的详细信息,例如视口的位置和比例,以及相对于屏幕上机器人的像素位置触摸的光点的像素位置)来正确地解释这一点(与用无线电控制器锁定在壁橱中的家伙不同) .

    我已经回答了你的问题了吗? :-)

    控制器是从用户获取输入的任何东西,用于使模型转换状态 . 尽量保持视图和控制器是分开的,但要意识到它们通常是相互依赖的,所以如果它们之间的边界模糊,即将视图和控制器作为单独的包可能不像你那样干净地分开就可以了 . 喜欢,但没关系 . 您可能必须接受控制器不会与视图完全分离,因为视图来自模型 .

    ......是否应在控制器中进行任何验证?如果是这样,我如何将错误消息反馈回View - 如果再次通过Model,或者Controller是否应该直接将其发送回View?如果在视图中完成验证,我应该在Controller中添加什么?

    我说链接视图和控制器应该自由交互而不通过模型 . 控制器接受用户的输入并应进行验证(可能使用来自模型和/或视图的信息),但如果验证失败,控制器应该能够直接更新其相关视图(例如错误消息) .

    对此进行的酸测试是问自己一个独立的视图(即另一个房间中通过网络观察机器人位置的人)是否应该看到任何其他人的验证错误(例如衣柜里的人)试图告诉机器人离开现场) . 通常,答案是否定的 - 验证错误阻止了状态转换 . 如果没有状态tranistion(机器人没有移动),没有必要告诉其他意见 . 壁橱里的那个人没有得到任何反馈,他试图导致非法过渡(没有视图 - 糟糕的用户界面),没有人需要知道这一点 .

    如果那个带触摸屏的人试图将机器人送出现场,他会得到一条很好的用户友好消息,要求他不要通过将其发送到检测区域来杀死机器人,但同样,没有其他人需要知道这一点 .

    如果其他视图确实需要了解这些错误,那么您实际上是在说来自用户的输入和任何产生的错误都是模型的一部分,整个事情有点复杂......

  • 58

    这是关于MVC基础知识的good article .

    它指出 ...

    控制器 - 控制器将与视图的交互转换为模型要执行的操作 .

    换句话说,就是你的业务逻辑 . 控制器响应用户在视图中采取的动作并做出响应 . 您在此处进行验证,如果验证失败或成功,则选择适当的视图(错误页面,消息框,等等) .

    还有另一个好处article at Fowler .

  • 0

    MVC模式只是想要你 separate the presentation (= view) from the buisiness logic (= model) . 控制器部分只会引起混淆 .

  • 1

    根据您的问题,我得到的印象是您对模型的角色有点模糊 . 模型固定在与应用程序相关的数据上;如果应用程序有数据库,那么Model的工作就是与它通信 . 它还将处理与该数据相关的任何简单逻辑;如果你有一条规则说明了所有情况下TABLE.foo ==“万岁!”和TABLE.bar ==“Huzzah!”然后设置TABLE.field =“W00t!”,然后您希望模型处理它 .

    Controller应该处理大部分应用程序的行为 . 那么回答你的问题:

    “我只需要调用Controller中的方法,就可以在视图中放置public void actionPerformed(ActionEvent e)吗?”

    我会说不 . 我会说应该住在控制器中; View应该简单地将来自用户界面的数据提供给Controller,并让Controller决定应该响应哪些方法 .

    “如果是这样,是否应在控制器中进行任何验证?”

    验证的大部分确实应由控制器完成;它应该回答数据是否有效的问题,如果不是,请将相应的错误消息提供给View . 实际上,您可以在View层中加入一些简单的健全性检查,以改善用户体验 . (我主要考虑Web环境,你可能希望在用户点击“提交”时弹出错误消息,而不是等到整个提交 - >进程 - >加载页面循环,然后告诉他们搞砸了 . )只是小心;你不想重复工作,而且在很多环境中(再次,我在考虑网络)你经常要把来自用户界面的任何数据视为一堆肮脏的污秽直到你确认它确实合法为止 .

    “如果是这样,我如何将错误消息反馈回视图 - 如果再次通过模型,或者控制器是否应该直接将其发送回View?”

    您应该设置一些协议,其中View不一定知道接下来会发生什么,直到Controller告诉它 . 在用户敲击该按钮后,您会显示哪些屏幕? View可能不知道,并且Controller可能不知道它直到它查看它刚刚获得的数据 . 它可能是“按预期转到其他屏幕”或“停留在此屏幕上,并显示此错误消息” .

    根据我的经验,模型和视图之间的直接通信应该非常非常有限,并且视图不应该直接改变模型的任何数据;这应该是Controller的工作 .

    “如果在视图中完成验证,我应该在Controller中放置什么?”

    往上看;真正的验证应该在Controller中 . 希望你现在知道应该在Controller中放置什么 . :-)

    值得注意的是,它可以在边缘处变得有点模糊;与大多数像软件工程一样复杂的东西一样,判断调用也会比比皆是 . 只需使用您的最佳判断,尝试在此应用程序中保持一致,并尝试将您学到的课程应用于下一个项目 .

  • 463

    实际上,我从未发现控制器概念是特别有用的 . 我在代码中使用严格的模型/视图分离,但没有明确定义的控制器 . 这似乎是一种不必要的抽象 .

    就个人而言,成熟的MVC似乎是工厂设计模式,因为它很容易导致混乱和过于复杂的设计 . 不要成为architecture astronaut .

  • 23

    控制器实际上是View的一部分 . 它的工作是确定需要哪些服务来完成请求,将View中的值解组为服务接口所需的对象,确定下一个View,并将响应编组回到下一个View可以使用的表单中 . 它还处理抛出的任何异常,并将它们呈现为用户可以理解的视图 .

    服务层是了解用例,工作单元和模型对象的东西 . 对于每种类型的视图,控制器都是不同的 - 对于桌面,基于浏览器,Flex或移动UI,您将没有相同的控制器 . 所以我说它确实是UI的一部分 .

    面向服务:这就是工作的地方 .

  • 3

    控制器主要用于视图和模型之间的协调 .

    不幸的是,它有时最终会与视图混合在一起 - 在小应用程序中虽然这不是太糟糕 .

    我建议你把:

    public void actionPerformed(ActionEvent e)
    

    在控制器中 . 然后,视图中的动作侦听器应委托给控制器 .

    至于验证部分,你可以把它放在视图或控制器中,我个人认为它属于控制器 .

    我肯定会建议你看一下Passive View和Supervising Presenter(这实际上是Model View Presenter分成的 - 至少是Fowler) . 看到:

    http://www.martinfowler.com/eaaDev/PassiveScreen.html

    http://www.martinfowler.com/eaaDev/SupervisingPresenter.html

  • 7

    以下是我使用的经验法则:如果它是我将专门用于此页面上的操作的过程,则它属于控制器,而不是模型 . 该模型应该只为数据存储提供连贯的抽象 .

    在使用由开发人员编写的大型web应用程序之后我想出了这个,他们认为他们被理解为MVC但实际上并没有 . 他们的“控制器”被简化为八行调用静态类方法,这些方法通常被称为其他方法: - 使其模型不仅仅是创建命名空间的方法 . 正确地重构这三件事:将所有SQL转移到数据访问层(也就是模型),使控制器代码更冗长但更容易理解,并将旧的“模型”文件减少为零 . :-)

  • 0

    另请注意,每个Swing小部件都可以被认为包含三个MVC组件:每个都有一个模型(即ButtonModel),View(BasicButtonUI)和Control(JButton本身) .

  • 15

    你对控制器中的内容基本上是正确的 . 这是模型与View交互的唯一方式 . actionperformed可以放在View中,但实际的功能可以放在另一个充当Controller的类中 . 如果您要这样做,我建议您查看命令模式,这是一种抽象所有具有相同接收器的命令的方法 . 对不起题外话题 .

    无论如何,正确的MVC实现将仅具有以下交互:模型 - >视图视图 - >控制器控制器 - >视图

    可能存在另一个交互的唯一地方是如果您使用观察者更新视图,则View将需要向Controller询问其所需的信息 .

  • 3

    据我了解,Controller将用户界面操作转换为应用程序级操作 . 例如,在视频游戏中,Controller可能会将“将鼠标移动这么多像素”转换为“想要在这样一个方向上查看 . 在CRUD应用程序中,翻译可能是”点击这样一个按钮“ “打印这个东西”,但概念是一样的 .

  • 9

    我们这样做,使用控制器主要处理和响应用户驱动的输入/操作(和_Logic用于其他一切,除了视图,数据和明显的_Model东西):

    (1)(响应,反应 - webapp“响应用户”做什么)Blog_Controller

    • 主()

    • handleSubmit_AddNewCustomer()

    • verifyUser_HasProperAuth()

    (2)(“业务”逻辑,webapp“思考”的内容和方式)Blog_Logic

    • sanityCheck_AddNewCustomer()

    • handleUsernameChange()

    • sendEmail_NotifyRequestedUpdate()

    (3)(视图,门户网站,webapp如何“出现”)Blog_View

    • genWelcome()

    • genForm_AddNewBlogEntry()

    • genPage_DataEntryForm()

    (4)(仅在每个Blog *类的_construct()中获取的数据对象,用于将所有webapp / inmemory数据保存在一起作为一个对象)Blog_Meta

    (5)(基本数据层,对DB的读/写)Blog_Model

    • saveDataToMemcache()

    • saveDataToMongo()

    • saveDataToSql()

    • loadData()

    有时我们会对C或L中放置方法的位置感到有些困惑 . 但是模型是坚如磐石的,清晰的,并且由于所有内存中的数据都存在于_Meta中,因此它也是一个明智的选择 . . 顺便说一下,我们最大的飞跃就是采用_Meta使用,因为这清除了来自各种_C,_L和_Model对象的所有瑕疵,使得它在精神上易于管理,而且,一下子,它给了我们什么样的东西 . 称为“依赖注入”,或者是一种传递整个环境以及所有数据的方法(其奖励很容易创建“测试”环境) .

相关问题