【问题标题】:MVC: pass model / model data to a view from a controller?MVC:将模型/模型数据从控制器传递到视图?
【发布时间】:2010-11-25 03:51:55
【问题描述】:

如果视图需要访问模型中的数据,您认为控制器应该:

a) 将模型传递给视图
b) 将模型的数据传递给视图
c) 两者都不是;这不应该是控制器的关注点。让视图直接访问模型以检索数据。只让控制器给视图需要的一些参数来过滤模型中的数据。
d) 视情况而定。
e) 以上都不是,但是[...]

谢谢

在 cmets 对用户删除的答案进行了一些辩论之后,也许这需要澄清。我对 MVC 架构的看法偏向于 Zend Framework (php),其中控制器中的操作默认分配有默认视图。因此,决定哪个视图合适的不是模型,而是控制器。你觉得模型应该规定什么视图是合适的吗?我认为让视图基于模型构建的唯一方法是让控制器将模型传递给视图。是否有其他技术可以让视图在不涉及控制器的情况下访问模型?还是让控制器将模型传递给视图,以便可以根据模型属性构建视图是完全没问题的?

【问题讨论】:

    标签: model-view-controller language-agnostic design-patterns


    【解决方案1】:

    e) 以上都不是;传入视图优化的“ViewModel”。

    ASP.NET MVC 中的示例:-

    public ActionResult Details(int id)
    {
      Product p = ProductService.GetProductById(id);
    
      if(p == null) { return RedirectToAction("Index"); }
    
      ProductViewModel model = new ProductViewModel(p);
      return View(model);
    }
    

    a) 和 b) 都“会”受 d) 约束。永远不会 c)。

    通常,ViewModel 只是封装了模型(如果没有发生任何复杂的事情,您的视图可以直接通过 ProductViewModel.Product 访问模型)。但是,如果视图需要对模型执行任何复杂的操作,则由 ViewModel 负责,而不是由 Controller 或 View 负责。

    这可以让您的关注点保持良好和隔离。你的控制器并不关心你的视图需要什么特定的数据(除了它渲染产品的一些细节之外),或者特别是你的视图需要什么格式的数据。你的视图不依赖于你的实现细节模型。您的模型不必关心它是如何被查看的。如果您有两个呈现产品的视图(例如 Create、Edit、Summary、MoreDetails 等),这些视图可以具有不同的 ViewModel 以仅公开每个特定视图所需的数据,因此您的视图不会相互依赖。可爱的:)

    从不同的角度进一步阅读:-

    http://www.thoughtclusters.com/2007/12/datamodel-and-viewmodel/

    http://stephenwalther.com/blog/archive/2009/04/13/asp.net-mvc-tip-50-ndash-create-view-models.aspx

    http://www.nikhilk.net/Silverlight-ViewModel-MVC.aspx

    我认为 ViewModel 是一个特别的 .NET 事物,但我完全看不出为什么该模式不能在 PHP 中使用。

    希望这会有所帮助。

    【讨论】:

    • 我非常喜欢这个想法。谢谢你。我将考虑如何将它整合到基于 Zend 框架的应用程序中。我仍然担心的(但也许你的 View 对象与 ZF 的 View 对象不同)是控制器仍然需要决定应该渲染什么视图。在纯粹的 MVC 意义上,模型(或者在您的情况下是 ViewModel)不应该决定它需要正确呈现的视图吗?在您的示例中,控制器看起来仍然负责将正确的 View 与正确的 ViewModel 耦合。这个假设正确吗?
    • 如果您要根据模型的某些方面或请求的某些方面显示不同的视图,是的,绝对是,控制器负责选择要呈现的视图,并且为该视图提供一些模型对象/数据。我不确定 Zend 是如何处理它的,如果它中间有一层可以进行拾取,那就坚持下去。在 ASP.NET MVC 中,您只需指定要显示的视图的名称(例如 return View("Index");),受一些假设、约定和默认值的约束。
    • 你可以看到一个很好的关于ViewModel在PHP中实现的教程:MVC-meet-the-viewmodel-pattern
    【解决方案2】:

    理想情况下,它应该“将模型的数据传递给视图”,这样视图就不需要知道模型的任何显式结构,从而更加可重用和设计人员友好。

    但实际上,“将模型传递给视图”效果很好。大多数时候,无论如何您都需要一个新视图,因为客户从不共享最喜欢的颜色(如果您知道我的意思:-),因此视图的可重用性并不能证明需要大量繁琐的代码来将数据从模型复制到视图。

    您应该更关心的是控制器本身的模块化,因为许多网站确实共享通用功能(控制器),例如网络论坛或新闻列表,但不具有外观(视图)

    【讨论】:

    • 你不觉得有点违反单一职责原则吗? - 如果您执行 a),那么如果模型的实现细节发生更改,则视图必须更改。如果您这样做 b) 则更糟糕的是,如果模型或视图发生更改,控制器必须更改。通过将将域模型转换为适合 View 使用的格式的代码限制在 ViewModel 中,您可以将 View 与 Model 中的更改隔离开来,并将您的 Controller 与任何一个中的更改隔离开来。
    • 当然,有一些需要担心的原则...... KISS 原则怎么样? ... ViewModel 在 WPF/Silverlight 上下文中工作,因为您有一个超级绑定引擎自动为您移动数据,但在这种情况下,它是一个 HTTP 环境,数据的移动速度要慢得多(即没有实时更新,没有闪亮的动画等) - 输入这么多代码只是为了打印一些段落,因为 HTML 只会在以后制造不必要的维护噩梦。 -- 我想不出一个额外的 ViewModel 层值得它增加的复杂程度的例子。
    • 相反,我觉得将视图与模型分离减少您以后的“维护噩梦”。我发现基于“输入这么多代码”的反对意见是完全错误的。代码必须去某个地方。在您的解决方案中,它进入控制器,与控制器的职责纠缠在一起。在我的,它有自己的家。相同的“代码量”。少耦合。我发现关于移动数据的反对同样是错误的——数据在控制器和视图之间移动(或在返回途中的 FormCollection 和控制器之间移动),而不是在客户端/服务器之间移动。
    • @lain Galloway 我想你没抓住我的意思。我的代码没有进入控制器,我只是重用没有“ViewModel”的模型类。记住“代码必须去某个地方”,模型中的某个地方已经,所以大多数时候添加“ViewModel”层是代码重复/不合理的复杂性。更少的“代码量”。更少的“复杂性”,是的,更多的“耦合”权衡。
    • @lain Galloway 我所说的“HTTP 环境”是指对于单个请求,您只需将数据从数据库移动一次到控制器到视图,仅此而已。 -- 与 WPF 场景不同,用户只需移动鼠标即可移动数据……因此,如果您没有有 ViewModel,您最终会得到大量仅复制的动画/交互代码从一个地方到另一个地方的值只是为了更新 UI——在这种情况下,将这些代码分解为“ViewModel”不仅合适,而且应该主动完成。 -- 但是当你只是输出 HTML 时,我认为这是不合理的。
    【解决方案3】:

    这是我知道,但我在我正在处理的项目中遇到了这个问题。我从 a) 开始——为了简单起见——现在遇到了障碍。

    我正在尝试这种方法:

    e) 以上都不是;传入视图优化的“ViewModel”。

    因为它避免了模型类(其实例是“事务对象”)和视图的膨胀。例如,您可能需要渲染具有特定小数位数的数字(这是我现在遇到的问题)。

    使用中间“ViewModel”这很容易 - 您只需编写相关的 ViewModel“getXXX” 方法以返回您希望格式化的数字。

    如果您只是将模型直接传递到视图中,则每次使用此图时都需要在视图中指定它(与 DRY 相悖 - 不要重复自己),或者,将渲染方法添加到您的模型类(与仅用于一个目的的类相反)。

    干杯

    【讨论】:

      【解决方案4】:

      a) 将模型传递给视图

      否则控制器会通过筛选模型来操作视图。这就是“b)将模型的数据传递给视图”中会发生的情况。 b) 选项在纯 MVC 模式中甚至没有任何意义。毕竟,模型就是数据。如果模型为了消费而改变,一个视图已经被应用,无论你选择在控制器中做它并将它作为控制器函数传递。当你有不同的看法时会发生什么?控制器是否以不同方式筛选其数据?您很快就会有两个模型视图,控制器子视图和视图本身。

      【讨论】:

      • 哦,是的...至于 c),它是控制器的角色来控制,d) 不,一致性是我们使用模式的原因,而 e) 根本不是模式。
      • 这是 Reenskaug 的文章...folk.uio.no/trygver/2003/javazone-jaoo/MVC_pattern.pdf ...描述了他在 1978 年在 MVC 方面的 Xerox PARC 工作。
      • 正如我对伊恩的回答所评论的那样:我有点困惑。您的两个答案似乎都假设控制器仍然负责将模型绑定到正确的视图。它是否正确?或者您的提议是否假设视图能够根据它收到的模型确定它应该使用什么明确的视图(或模板)来渲染? (更符合 Zend 框架的 ViewRenderer 对象;顾名思义:一个渲染器,您可以告诉它应该渲染哪个视图,通常由控制器的操作方法决定)。
      • 控制器负责将模型绑定到正确的视图。控制器负责事务,因此根据事务的结果,通过选择适当的视图进行后续显示。这在实践中很难,我自己通过在视图中放置分支逻辑来打破它,但要避免。
      • 看看我的帖子也在这里...stackoverflow.com/questions/140098/… ...并记住 MVC 是一种模式。模式是指导。如果你是一个聪明的编码员,你会调整它以最好地处理你的情况。还行吧。如果这是一个固定的规则,计算机就不需要你告诉它该做什么。
      【解决方案5】:

      对我来说就是 e)。

      正如这里已经提到的,理想情况下视图和模型是分离的。我更喜欢实现这一点的方式是为模型设置一个 viewHelper。这具有视图可用于获取数据的 API。现在视图不受模型更改的影响,并且视图不需要“获取”模型的内部结构。这些被 viewHelper 隐藏起来。

      示例:

      class Post {
          public function export(ViewHelper $helper) {} // I try to avoid getters when I can
      }
      
      class PostViewHelper {
          public function getTitle($parameters) {} // title of current post in the loop
      }
      
      class PostView {
          private $helpers = array();
          public function loadTemplate($path) {}
          public function addHelper(ViewHelper $helper, $name) {}
          public function __get($key) {} // if exists $this->helper[$key] etc
      }
      

      在模板中

      <h1><?php $this->post->getTitle(); ?></h1>
      

      您可能希望以不同的方式实现这一点。但我的观点是视图和模型是如何解耦的,引入了一个中间 viewHelper 来创建视图/模板 API。

      【讨论】:

        【解决方案6】:

        我认为没有那么复杂。 a 或 b。

        控制器的工作是管理关系。它找到模型和视图,并为视图提供完成其工作所需的所有模型数据。而已。 MVC 并没有规定数据采用的确切形式。

        (a) 从简单开始。将模型对象直接传递给视图是很自然的。如果你有一个关于 Foo 的页面,只需传递 Foo。

        (b) 但有时——而且只是有时——您创建一个值对象/DTO 来将数据获取到视图(上面称为 ViewModel)。 当视图和本机模型不匹配时执行此操作,例如摘要视图。如果视图呈现 1,000,000 个对象的摘要,您不想将视图交给模型对象;您想向视图提供 1,000,000 个对象的摘要。

        确切的实现实际上取决于您使用的语言和框架。但我认为这些指南是一个好的开始。

        【讨论】:

        • +1,虽然我要指出,在许多情况下,维护 b) (我称它为 e)是一件好事,但我们基本上提出了同样的事情)并且有你的 DTO 只是封装你的 Foo。虽然这是一个简单的重构,所以您可能只需应用 YAGNI 并继续使用它。此外,ASP.NET MVC 中的 ViewModel 并不完全是 DTO(ViewModel 可以有行为,DTO 只是 getter/setter),但这变得非常挑剔:P
        【解决方案7】:

        嗯嗯。

        我真的看不出 a 和 b 之间的区别,除了你将如何传递数据的一些技术性问题。

        通常您将数据映射与模型中的一些数据一起传递给视图

        【讨论】:

        • 这是一个 SRP 问题。重要的区别在于,如果你这样做 a) View 耦合到 Model,如果你这样做 b) Controller 耦合到 View。如果您使用 ViewModel,那么 ViewModel 明确负责弥合 View 和 Model 之间的差距,从而允许 Model、View 和 Controller 完成它们的工作而不用担心耦合 - 特别是如果您使用工厂模式来创建ViewModel。
        猜你喜欢
        • 1970-01-01
        • 2015-05-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-20
        • 1970-01-01
        • 2021-02-24
        相关资源
        最近更新 更多