标准的“模型视图控制器”模式和微软的模型/视图/视图模型模式之间有区别吗?


当前回答

MVVM

视图➡视图模型➡模型

视图有对ViewModel的引用,反之则没有。 ViewModel有对模型的引用,反之则没有。 视图没有对模型的引用,反之亦然。

如果你正在使用一个控制器,它可以有一个视图和视图模型的引用,尽管控制器并不总是必要的,就像在SwiftUI中演示的那样。 数据绑定:我们为ViewModel属性创建侦听器,这样数据就可以通过视图模型从视图流向模型。虽然这些引用是单向的:View↔ViewModel↔Model,但数据需要流动:View↔ViewModel。视图如何通过读取自己的属性从模型中获取数据是很清楚的。数据绑定是如何在视图中检测事件并将它们反馈给模型。

class CustomView: UIView {
  var viewModel = MyViewModel {
    didSet {
      self.color = viewModel.viewColor
    }
  }

  convenience init(viewModel: MyViewModel) {
    self.viewModel = viewModel
  }
}


struct MyViewModel {
   var viewColor: UIColor {
      didSet {
         colorChanged?() // This is where the binding magic happens.
      }
   }
   
   var colorChanged: ((UIColor) -> Void)?
}


class MyViewController: UIViewController {

   let myViewModel = MyViewModel(viewColor: .green)
   let customView: CustomView!

   override func viewDidLoad() {
      super.viewDidLoad()

      // This is where the binder is assigned.
      myViewModel.colorChanged = { [weak self] color in 
        print("wow the color changed")
      }
      customView = CustomView(viewModel: myViewModel)
      self.view = customView
   }
}

设置的差异

业务逻辑保存在MVC的控制器中,MVVM的ViewModels中。 在MVC中,事件直接从视图传递到控制器,而在MVVM中,事件从视图传递到ViewModel再到控制器(如果有的话)。

共同的特征

MVVM和MVC都不允许视图直接向模型发送消息。 两者都有自己的模型。 两人都有自己的观点。

MVVM的优点

因为viewmodel包含业务逻辑,所以它们是更小的具体对象,便于进行单元测试。另一方面,在MVC中,业务逻辑在ViewController中。如果不同时测试所有的方法和侦听器,你怎么能相信视图控制器的单元测试是全面安全的呢?您不能完全相信单元测试结果。 在MVVM中,由于业务逻辑从控制器中被抽取到原子ViewModel单元中,ViewController的大小缩小了,这使得ViewController代码更加清晰。

MVC的优点

在控制器中提供业务逻辑减少了对分支的需求,因此语句更有可能在缓存上运行,这比将业务逻辑封装到ViewModels中性能更好。 在一个地方提供业务逻辑可以加速不需要测试的简单应用程序的开发过程。我不知道什么时候不需要检查。 对于新开发人员来说,在ViewController中提供业务逻辑更容易考虑。

其他回答

MVVM模型-视图视图模型类似于MVC,模型-视图控制器

控制器被替换为ViewModel。ViewModel位于UI层下面。ViewModel公开视图所需的数据和命令对象。你可以把它想象成一个容器对象视图从它那里获取数据和动作。ViewModel从模型中提取数据。

Russel East写了一篇博客,详细讨论了为什么MVVM不同于MVC

MVVM是表示模型模式的改进(有争议)。我说有争议,因为唯一的区别是WPF如何提供数据绑定和命令处理的能力。

从实用的角度来看,MVC(模型-视图-控制器)是一种模式。然而,当MVC作为ASP.net MVC使用时,当与实体框架(EF)和“强大的工具”相结合时,它是一种非常强大的、部分自动化的方法,用于将数据库、表和列引入web页面,用于完整的CRUD操作或仅用于R(检索或读取)操作。至少在我使用MVVM时,视图模型与依赖于业务对象的模型交互,这些业务对象是“手工制作”的,经过大量的努力,能够获得与EF提供的“开箱即用”一样好的模型是很幸运的。从实用编程的角度来看,MVC似乎是一个不错的选择,因为它提供了许多开箱即用的实用程序,但仍然有可能添加一些花哨的功能。

在web开发中,MVC是服务器端,MVVM是客户端(浏览器)。

大多数情况下,浏览器中的MVVM使用javascript。mvc有很多服务器端技术。

MVC是受控环境,MVVM是响应式环境。

在受控的环境中,你应该有更少的代码和一个通用的逻辑源;它应该始终存在于控制器中。然而;在web世界中,MVC很容易被分为视图创建逻辑和视图动态逻辑。创建在服务器上,动态在客户端上。你在ASP中经常看到这种情况。与AngularJS结合在一起,而服务器将创建一个视图并传入一个模型并将其发送给客户端。然后客户端将与视图交互,在这种情况下,AngularJS将作为本地控制器介入。一旦提交模型或新模型,就会被传递回服务器控制器并进行处理。(因此循环继续,当使用套接字或AJAX等时,这种处理有很多其他的翻译,但总体上架构是相同的。)

MVVM是一个响应式环境,这意味着您通常编写的代码(例如触发器)将基于某些事件激活。在MVVM蓬勃发展的XAML中,使用内置的数据绑定框架可以轻松完成这一切,但如前所述,这将适用于任何视图、任何编程语言的任何系统。它不是多发性硬化症特有的。ViewModel会触发(通常是属性改变事件),View会根据你创建的触发器对它做出反应。这可能是技术性的,但底线是视图是无状态的,没有逻辑。它只是基于值更改状态。此外,viewmodel是无状态的,逻辑很少,而模型是逻辑本质为零的状态,因为它们应该只维护状态。我将其描述为应用程序状态(模型)、状态转换器(ViewModel),然后是可视状态/交互(视图)。

在MVC桌面或客户端应用程序中,你应该有一个模型,该模型应该由控制器使用。基于模型,控制器将修改视图。视图通常与带有接口的控制器绑定,以便控制器可以使用各种视图。在ASP。NET中的MVC逻辑在服务器上略微向后,因为控制器管理模型并将模型传递给选定的视图。然后,视图会根据模型填充数据,并拥有自己的逻辑(通常是另一个MVC集,例如用AngularJS完成的)。人们会争论并将其与应用程序MVC混淆,并试图同时做这两件事,而维护项目最终将成为一场灾难。使用MVC时,总是把逻辑和控件放在一个位置。不要在视图后面的代码中(或通过web的JS在视图中)写视图逻辑来容纳控制器或模型数据。让控制器改变视图。视图中应该存在的唯一逻辑是通过它所使用的接口创建和运行所需的任何逻辑。提交用户名和密码就是一个例子。无论是桌面还是网页(在客户端),只要视图触发提交动作,控制器就应该处理提交过程。如果做得正确,你总是可以很容易地找到MVC web或本地应用程序。

MVVM是我个人最喜欢的,因为它完全是反应性的。如果模型改变了状态,ViewModel会监听并转换状态,就这样!!然后,视图监听ViewModel的状态变化,并根据来自ViewModel的转换进行更新。有些人称之为纯粹的MVVM,但实际上只有一个,我不在乎你怎么争论,它总是纯粹的MVVM,其中视图完全不包含逻辑。

这里有一个小例子:假设你想要在按下按钮时滑动菜单。在MVC中,你的界面中会有一个MenuPressed动作。当你点击菜单按钮时,控制器会知道,然后告诉视图根据另一个接口方法(如SlideMenuIn)在菜单中滑动。为什么要往返?如果控制器决定你不能或想要做别的事情,这就是原因。控制器应该负责视图,视图什么都不做,除非控制器这么说。然而;在MVVM中,动画中的幻灯片菜单应该是内置的和通用的,而不是被告知滑动它将基于一些值这样做。它监听ViewModel当ViewModel说IsMenuActive = true时动画就会发生。现在,说到这里,我想非常清楚地说明另一点,请注意。IsMenuActive可能是糟糕的MVVM或ViewModel设计。在设计ViewModel时,你永远不应该假设视图会有任何特性,而只是传递转换后的模型状态。这样,如果你决定更改视图以删除菜单,只是以另一种方式显示数据/选项,ViewModel不会关心。那么您将如何管理菜单呢?当数据有意义时,就是这样。因此,一种方法是给菜单一个选项列表(可能是内部ViewModels的数组)。如果该列表有数据,菜单就知道通过触发器打开,如果没有,它就知道通过触发器隐藏。你只是在ViewModel中有菜单的数据。不要决定在ViewModel中显示/隐藏数据。简单地转换模型的状态。通过这种方式,视图是完全响应式和通用的,可以在许多不同的情况下使用。

如果你还不熟悉它们的架构,那么这一切可能完全没有意义,因为你会在网上发现很多不好的信息,学习起来可能会非常混乱。

所以…要做到这一点,需要记住以下几点。预先决定如何设计应用程序,并坚持下去。

如果你使用MVC,这很好,那么确保你的控制器是可管理的,并完全控制你的视图。如果你有一个大的视图,可以考虑在视图中添加具有不同控制器的控件。只是不要将这些控制器级联到不同的控制器上。维护起来非常令人沮丧。花点时间把东西分开设计,让它们作为独立的组件工作……并且总是让控制器告诉模型提交或持久化存储。MVC的理想依赖设置是视图←控制器→模型或ASP。模型←视图↔控制器→模型(从控制器到视图,模型可以相同,也可以完全不同)…当然,此时唯一需要知道的是视图中的控制器,主要是为了端点参考,以知道在哪里返回传递模型。

如果你做MVVM,我祝福你善良的灵魂,但要花时间做对!不要使用接口。让视图根据值来决定它的外观。使用模拟数据处理视图。如果你最终有一个视图显示你的菜单(如例),即使你不想要它的时候,那么好。你的视图按照它应该的方式工作,并根据它应该的值做出反应。只需向触发器添加一些需求,以确保ViewModel处于特定的转换状态时不会发生这种情况,或者命令ViewModel清空该状态。在你的ViewModel中,不要用内部逻辑删除它,就好像你在决定视图是否应该看到它一样。记住,你不能假设ViewModel中有或没有菜单。最后,模型应该允许您更改和存储状态。这是验证和所有事情发生的地方;例如,如果模型不能修改状态,那么它将简单地将自己标记为脏或其他东西。当ViewModel意识到这一点时,它会转换脏的东西,然后视图会意识到这一点,并通过另一个触发器显示一些信息。视图中的所有数据都可以绑定到ViewModel,因此一切都可以是动态的,只有模型和ViewModel完全不知道视图将如何对绑定做出反应。事实上,模型也没有ViewModel的概念。当设置依赖项时,它们应该这样指向,而且只这样指向View→ViewModel→Model(这里还有一个边注…这可能也会引起争论,但我不在乎……不要将模型传递给视图,除非该模型是不可变的;否则用适当的ViewModel包装它。视图不应该看到模型周期。我说你看过什么演示或者你是怎么做的,这是错误的。)

这是我的最后一个建议……看看一个设计良好但非常简单的MVC应用程序,并对MVVM应用程序进行同样的操作。一个将有更多的控制,限制到零灵活性,而另一个将没有控制和无限的灵活性。

受控环境非常适合从一组控制器或(单个源)管理整个应用程序,而响应式环境可以被分解为单独的存储库,完全不知道应用程序的其余部分在做什么。微观管理vs自由管理。

如果我还没有把你弄糊涂,试着联系我……我不介意用插图和例子详细讨论这个问题。

在一天结束的时候,我们都是程序员,当我们编码的时候,我们内心就会出现混乱……所以规则会被打破,理论会改变,所有这些最终都将成为垃圾……但在大型项目和大型团队中工作时,就设计模式达成一致并实施它确实很有帮助。总有一天,它会让你开始时多迈出的一小步,在以后变成一笔突飞猛进的积蓄。