如何理解MVC及其变种

发布时间:2021-10-14 11:27:35 作者:iii
来源:亿速云 阅读:137

这篇文章主要介绍“如何理解MVC及其变种”,在日常操作中,相信很多人在如何理解MVC及其变种问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何理解MVC及其变种”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

创建可维护的应用始终是构建应用的真正的长期挑战。

不久以前,我还为一家公司工作过,其核心业务应用是拥有数千家公司客户的 SaaS 平台。这个至关重要的应用已经开发了三年,代码文件中混杂着  HTML、CSS、业务逻辑和  SQL。果然,在发布两年之后,公司决定完全重写这个应用。尽管这些情况时有发生,但如今我们许多人都知道这是不对的以及该如何避免。

然而,在20世纪70年代,职责混杂还是常见的实践,人们还在寻找更好的解决办法。随着应用程序复杂度的增长,修改 UI  必然也会引起业务逻辑的修改,修改越发复杂,耗费的时间也越来越多,还可能带来更多的问题(因为修改的代码更多了)。

MVC 因此应运而生,它提出前端和后端之间的“关注点分离”来解决上述问题。

1979 – Model-View-Controller

如何理解MVC及其变种

为了解决上述问题,Trygve Reenskaug 于1979 年提出了 MVC 模式来分离关注点,将 UI 和业务逻辑隔离。该模式当时被应用于1973  就已经出现的桌面图形界面的开发。

MVC 模式将代码拆分成了三个概念单元:

模型可以是单个对象(相当无趣),也可以是对象组成的某种结构。——Trygve Reenskaug 1979, MVC

最初的 MVC 模式还有其它一些需要了解的的重要概念:

现在我所熟知的 HTTP 请求响应范式并没有使用最初的 MVC 风格。这是因为,按照原始的设想,数据从 View 流向  Controller,这和我熟悉的一样,但另一边,数据直接从 Model 流向 View,并没有经过 Controller。

而且,在现在的请求响应范式中,当数据库中的数据发生变化时,并不会触发浏览器中展示 View 的更新(尽管可以用 Web Socket  实现)。要看到更新后的数据,用户需要发起一次新的请求,而更新的数据总是会通过 Controller 返回。

1987/2000 – PAC/Hierarchical Model-View-Controller

如何理解MVC及其变种

PAC 又称 HMVC,在 UI 片段控件化的上下文中它能带来更好的模块化拆分。

例如,我们会发现 View 的一部分被其它一些 View 以同样的格式使用,甚至直接就在同一个 View 重复使用。一个实际的例子就是网页展现 RSS  订阅内容的片段,它可以被其它页面重用。

如果使用 HMVC,处理主请求的 Controller 会将子请求转发给其它 Controller 让这些控件进行渲染,然后在主 View  的渲染中合并它们。

在 HTTP 请求/响应范式的上下文里,我自己也曾遇到过几次这种情况,但我发现了一个更简单的方法,即让 UI 向可以渲染控件的 Controller 发起  AJAX 调用。在保持模块化优势的同时并没有增加嵌套 Controller 调用带来的复杂性,另一个优势就是这些子请求可以使用像 Varnish  这样的缓存。

1996 – Model-View-Presenter

如何理解MVC及其变种

MVC 模式给当时的编程范式注入了一剂强心针。然而,随着应用程序复杂度的增加,需要更进一步地解耦。

1996 年,IBM 的子公司 Taligent 公开了他们基于 MVC 的 模式 MVP。其思想是将 Model 对 UI 的关注更彻底地分离:

这更接近我所见到的现在的请求/响应范式:数据流始终要经过 Controller/Presenter。不过,Presenter  仍然不会主动更新视图,它始终需要执行一次新的请求才能让变化可见。

MVP 中的 Presenter 又被称为 Supervisor Controller。

2005 – Model-View-ViewModel

如何理解MVC及其变种

由于应用程序的复杂性还在增加,2005 年微软的 WPF 和 Silverlight 架构师 John Gossman 又提出了 MVVM  模式,目标是进一步将 UI 设计从代码中分离出来,并提供 View 到数据模型的数据绑定机制。

[MVVM] 是 [MVC] 的变种,专为现代 UI 开发平台设计。现代 UI 开发中,View 是由设计师负责而不是由传统意义上的开发者负责。[…]  开发应用程序 UI 使用的工具、语言以及使用它们的人都和业务逻辑以及数据后端有着天壤之别。——John Gossman 2005, Introduction  to Model/View/ViewModel pattern

Controller 被 ViewModel “取代”:

[View] 对键盘快捷键进行编码,而且控件自行管理与输入设备的交互,这本该是 MVC 中的 Controller 的职责(现代 GUI 开发中  Controller 的变化说来话长...我认为它只是淡出了开发者的实现。它始终都存在着,而我们不需要像1979年那样去思考它)。——John Gossman  2005, Introduction to Model/View/ViewModel pattern

MVVM 背后的思想是:

和最初的 MVC 模式的情况相仿,对传统的请求/响应范式来说这种方法是行不通的,因为 ViewModel 无法主动地更新 View(除非使用 Web  Socket),而 MVVM 对这一点是有要求的。还有,根据我的经验,ViewModel 的属性和 View 使用的数据做到完全匹配并不是 Controller  的常见实践。

Model-View-Presenter-ViewModel

如何理解MVC及其变种

当构建云原生的复杂企业应用时,我倾向于将应用的 UI 结构合理地设计成 M-V-P-VM,这里的 View Model 是 Martin Fowler 在  2004 年提出的 Presentation Model,。

Model

一组包含业务逻辑和用例的类。

View

一个模板,模板引擎用它来生成 HTML;

ViewModel(又叫做 Presentation Model)

从查询中接收(或者从 Model 实体中提取)原始数据,持有这些会模板会用到的数据。它还要封装复杂的展现逻辑,来简化模板。我发现运用 ViewModel  十分重要,因为我们绝不会想在模板中使用实体。这样我们才能将 View 和 Model 完全隔离开:

Presenter

接收 HTTP 请求,触发命令或查询,使用查询返回的数据、ViewModel、模板和模板引擎生成 HTML 并将它返回给客户端。所有 View  的交互都要经过 Presenter。

下面是我实现的一个非常简单的例子:

<?php // src/UI/Admin/Some/Controller/Namespace/Detail/SomeEntityDetailController.php namespace UI\Admin\Some\Controller\Namespace\Detail; // use ... final class SomeEntityDetailController {     /**      * @var SomeRepositoryInterface      */     private $someRepository;        /**      * @var RelatedRepositoryInterface      */     private $relatedRepository;     /**      * @var TemplateEngineInterface      */     private $templateEngine;     public function __construct(         SomeRepositoryInterface $someRepository,         RelatedRepositoryInterface $relatedRepository,         TemplateEngineInterface $templateEngine     ) {         $this->someRepository = $someRepository;         $this->relatedRepository = $relatedRepository;         $this->templateEngine = $templateEngine;     }     /**      * @return mixed      */     public function get(int $someEntityId) {         $mainEntity = $this->someRepository->getById($someEntityId);         $relatedEntityList = $this->relatedRepository->getByParentId($someEntityId);         return $this->templateEngine->render(             '@Some/Controller/Namespace/Detail/details.html.twig',             new DetailsViewModel($mainEntity, $relatedEntityList)         );     } }

M-V-C-VM_-_Controller_example.php

<?php // src/UI/Admin/Some/Controller/Namespace/Detail/DetailsViewModel.php namespace UI\Admin\Some\Controller\Namespace\Detail; // use ... final class DetailsViewModel implements TemplateViewModelInterface {     /**      * @var array      */     private $mainEntity = [];     /**      * @var array      */     private $relatedEntityList = [];     /**      * @var bool      */     private $shouldDisplayFancyDialog = false;     /**      * @var bool      */     private $canEditData = false;     /**      * @param SomeEntity $mainEntity      * @param RelatedEntity[] $relatedEntityList      */     public function __construct(SomeEntity $mainEntity, array $relatedEntityList) {         $this->mainEntity = [             'name' => $mainEntity->getName(),             'description' => $mainEntity->getResume(),         ];         foreach ($relatedEntityList as $relatedEntity) {             $this->relatedEntityList[] = [                 'title' => $relatedEntity->getTitle(),                 'subtitle' => $relatedEntity->getSubtitle(),             ];         }                  $this->shouldDisplayFancyDialog = /* ... some complex conditional using the entities data ... */ ;                  $this->canEditData = /* ... another complex conditional using the entities data ... */ ;     }     public function getMainEntity(): array {         return $this->mainEntity;     }     public function getRelatedEntityList(): array {         return $this->relatedEntityList;     }     public function shouldDisplayFancyDialog(): bool {         return $this->shouldDisplayFancyDialog;     }     public function canEditData(): bool {         return $this->canEditData;     } }

M-V-C-VM_-_ViewModel_example.php

模板和 ViewModel 一一对应,意味着 View 只能被一个特定的 ViewModel 使用,反过来也一样。这会让我进一步思考,也许我们可以将模板和  ViewModel 封装成一个 View 对象,更有效地将 Controller 和模板以及 ViewModel 解耦,让它只依赖一个通用的 View  接口;但我还没有机会实验这个想法。

到此,关于“如何理解MVC及其变种”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

推荐阅读:
  1. 深入理解Spring MVC 思想
  2. 亲历WannaCry变种病毒

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

mvc

上一篇:Sqlite数据库的基本语法有哪些

下一篇:如何理解synchronized锁升级

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》