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


当前回答

使用MVC将强类型视图模型注入到视图中

控制器负责更新ViewModel并将其注入View中。(用于get请求) ViewModel是DataContext和视图状态(如最后选中的项目等)的容器。 模型包含数据库实体,非常接近数据库模式,它进行查询和过滤。(我喜欢EF和LINQ) 模型还应该考虑存储库和或将结果投影到强类型(EF有一个很好的方法……EF.Database。选择(querystring, parms)直接ADO访问注入查询和返回强类型。这解决了EF是缓慢的参数。EF并不慢! ViewModel获取数据并执行业务规则和验证 post后面的控制器将调用ViewModel post方法并等待结果。 控制器会将最新更新的视图模型注入到视图中。视图只使用强类型绑定。 视图仅仅呈现数据,并将事件发送回控制器。(参见下面的例子) MVC拦截入站请求,并将其路由到具有强数据类型的适当控制器

在这个模型中,不再有HTTP级别的与请求或响应对象的接触,因为MSFT的MVC机器对我们隐藏了它。

澄清上文第6项(应要求)…

假设ViewModel是这样的:

public class myViewModel{
     public string SelectedValue {get;set;}
public void Post(){
    //due to MVC model binding the SelectedValue string above will be set by MVC model binding on post back.
    //this allows you to do something with it.
    DoSomeThingWith(SelectedValue);
    SelectedValue = "Thanks for update!";
 }
}

这篇文章的控制器方法看起来像这样(见下文),注意mvm的实例是由MVC绑定机制自动实例化的。因此,您永远不必下拉到查询字符串层!这是MVC基于查询字符串为您实例化ViewModel !

[HTTPPOST]   
public ActionResult MyPostBackMethod (myViewModel mvm){
         if (ModelState.IsValid)
        {
               // Immediately call the only method needed in VM...
               mvm.Post()
        }
      return View(mvm);
}

注意,为了让上面的actionmethod像你想要的那样工作,你必须定义一个空CTOR来初始化post中没有返回的东西。回发也必须回发那些发生变化的名称/值对。如果缺少名称/值对,MVC绑定引擎就会做正确的事情,而这根本不是什么!如果发生这种情况,你可能会发现自己说“我正在丢失回post的数据”…

这种模式的优点是ViewModel做了所有“杂乱”的工作,接口到模型/业务逻辑,控制器只是一个路由器。这是SOC在起作用。

其他回答

首先,MVVM是使用XAML处理显示的MVC模式的发展。本文概述了这两者的一些方面。

模型/视图/视图模型架构的主要目的似乎是在数据(“模型”)之上,还有另一层非可视组件(“视图模型”),它将数据的概念更紧密地映射到数据的视图(“视图”)的概念。视图绑定的是ViewModel,而不是直接绑定模型。

我认为主要的区别之一是,在MVC中,你的V直接读取你的M,并通过C来操作数据,而在MVVM中,你的VM充当M代理,以及为你的V提供可用的功能。

如果我不是满口废话,我很惊讶没有人创建一个混合的,其中您的VM只是一个M代理,C提供所有功能。

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、MVP和MVC的剧本。故事开始于一个用户点击电影搜索应用程序中的“FIND”按钮…

用户:单击…

观点:那是谁?[MVVM|MVP|MVC]

用户:我只是点击了搜索按钮……

视图:好的,稍等... .(MVVM MVP | | MVC)

(视图调用ViewModel|Presenter|Controller…)

视图:嘿,ViewModel|Presenter|Controller,一个用户刚刚点击了搜索按钮,我该怎么做?(MVVM MVP | | MVC)

ViewModel|Presenter|Controller:嘿View,这个页面上有搜索词吗?(MVVM MVP | | MVC)

查看:是的,…在这里…" piano " [MVVM|MVP|MVC]

这是MVVM和MVP|MVC之间最重要的区别

演示者|控制器:谢谢视图,…同时我正在模型上查找搜索词,请给他/她一个进度条[MVP|MVC]

(演示者|控制器正在调用模型…)

ViewModel:谢谢,我会在模型上查找搜索词,但不会直接更新你。相反,如果有任何结果,我将触发事件来searchResultsListObservable。所以你最好仔细观察一下。(MVVM)

(当在searchResultsListObservable中观察任何触发器时,视图认为它应该向用户显示一些进度条,因为ViewModel不会在那上面与它对话)

——————————————————————————————

ViewModel|演示者|控制器:嘿,模型,你有匹配这个搜索词吗?: " piano " [MVVM|MVP|MVC]

Model: Hey ViewModel|Presenter|Controller, let me check…[MVVM|MVP|MVC]

(模型正在向电影数据库查询…)[MVVM|MVP|MVC]

(过了一会儿……)

————这是MVVM, MVP和MVC————-之间的分歧点

模型:我为你找到了一个列表,ViewModel|Presenter,这里是JSON“[{" name ": "钢琴老师","年份":2001},{" name ": "钢琴","年份":1993}]" [MVVM|MVP]

模型:有一些结果可用,控制器。我已经在我的实例中创建了一个字段变量,并将其填充为结果。它的名字是searchResultsList [MVC]

(演示者|控制器感谢模型并返回视图)[MVP|MVC]

主持人:感谢您的等待,View,我为您找到了匹配结果的列表,并将它们以像样的格式排列:["钢琴老师2001″,"钢琴1993 "]。同时,现在请隐藏进度条[MVP]

控制器:感谢等待视图,我已经询问了模型关于你的搜索查询。它说它已经找到了一个匹配结果列表,并将它们存储在其实例内名为“searchResultsList”的变量中。你可以从那里得到它。同时,现在请隐藏进度条[MVC]

ViewModel: searchResultsListObservable上的任何观察者都将被通知有这样一个像样格式的新列表:[" Piano Teacher 2001″," Piano 1993 "]。

视图:非常感谢主讲人[MVP]

视图:谢谢你“控制器”[MVC](现在视图在问自己:我应该如何将我从模型得到的结果呈现给用户?电影的制作年份是先写还是后写?)

视图:哦,在searchResultsListObservable中有一个新的触发器…,很好,有一个像样的列表,现在我只需要在列表中显示它。有了结果之后,我还应该隐藏进度条。(MVVM)

如果你感兴趣,我在这里写了一系列文章,通过实现一个电影搜索android应用程序来比较MVVM、MVP和MVC。

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

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

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