MVC&&MVP&&MVVM

MVC && MVP && MVVM

[TOC]

概念

架构模式

也叫架构风格,一个架构模式描述软件系统里的基本的结构组织或纲要。架构模式提供一些呈先定义好的子系统,指定它们的责任,并给出把它们组织在一起的法则和指南。一个架构模式常常可以分解成很多个设计模式的联合使用。MVC模式就属于架构模式。[7]

关键词

  • 纲要

  • 预先定义好的子系统

  • 指定子系统责任(职责边界)

  • 组织的法则和指南(协作的方式)

  • 设计模式的联合使用

业务逻辑[8]

业务逻辑是业务领域的逻辑,是提供业务服务的规则和流程

业务逻辑包括

  • 领域实体
  • 业务规则
  • 完整性约束
  • 业务流程

应用逻辑

应用逻辑是指视图和业务协作的一套机制

应用逻辑包括

  • 连接视图和业务
  • 串联业务
  • 业务切换
  • 业务管理

关注点分离

在讨论这几种架构模式之前,我们先讨论一个关注点分离的设计原则。

关注点分离 (Separation of concerns,SOC)是对只与「特定概念、目标」(关注点)相关联的软件组成部分进行「标识、 封装和操纵」的能力,即标识、封装和操纵关注点的能力。 是处理复杂性的一个原则。 由于关注点混杂在一起会导致复杂性大大增加,所以能够把不同的关注点分离开来,分别处理就是处理复杂性的一个原则,一种方法。[1]

从关注点分离的定义我们可以关注到有四个关键词:

  • 特定概念、目标

  • 标识

  • 封装

  • 操纵

从分离点关注的概念和关键词描述,可以映射到我们日常的一些软件设计方法,例如:

  • 职责分离
  • 4+1视图
  • 架构风格(架构模式)
  • 外观模式(从封装性关键词联想)

分层设计这种架构风格是关注点分离的一种实施例子,因此分层设计满足关注点分离原则,那我们对应上面说的四个词来阐述对于这几种架构模式的理解,以及如何影响软件的质量。

MVC

MVC 关注点分离出三个要素

  • Model(模型)
  • View(视图)
  • Controller(控制器)

下图是组件之间的交互[3]

MVC协作

figure [1]

职责说明

基于上图的交互,我们对MVC各个组件的职责进行一下说明

  • Model
  • View
  • Controller

在说职责之前,我们先想一下如何划分职责,可以达到职责单一,并且可以提高软件的复用性和可维护性。

View

  • 提供信息的展现界面

    对于同一份数据,可以通过不同的视图显示不同的效果

  • 接收用户的操作指令

在这里我想说下win32程序窗口创建的代码[4]

步骤:

  • 声明和填充WNDCLASSEX对象
  • 调用RegisterClassEx注册声明的窗口类型
  • 调用CreateWindow创建注册的窗口类型
  • 调用ShowWindow显示窗口
  • 创建消息循环,派发消息
  • 在WndProc回调函数处理通知的窗口消息(如下,基本的业务逻辑入口都在这个函数里面)
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
   PAINTSTRUCT ps;
   HDC hdc;
   TCHAR greeting[] = _T("Hello, Windows desktop!");

   switch (message)
   {
   case WM_PAINT:
      hdc = BeginPaint(hWnd, &ps);

      // Here your application is laid out.
      // For this introduction, we just print out "Hello, Windows desktop!"
      // in the top left corner.
      TextOut(hdc,
         5, 5,
         greeting, _tcslen(greeting));
      // End application specific layout section.

      EndPaint(hWnd, &ps);
      break;
   case WM_DESTROY:
      PostQuitMessage(0);
      break;
   default:
      return DefWindowProc(hWnd, message, wParam, lParam);
      break;
   }

   return 0;
}

该代码是一个回调方法,用户提供给系统通知窗口相关的事件,这个和Android设置一个回调类似,不同的事,窗口事件包括了用户的指令(点击)和窗口本身的内部事件(绘制,销毁等)

如果不做任何代码的优化,在一个复杂的用户界面,这个函数会变的异常庞大,考虑到很多的业务逻辑是可以复用的,因此会提取业务相关的指令汇集到一个集中的处理。

这样的话,WndProc会出现一些变化,那假设我们理解的MVC各个组件职责如下图

WndProc职责抽离

Figure 2

Controller && Model

根据软件架构设计的分层[5]介绍的四层模型,核心是问题领域层,问题领域层对应业务实体层和业务逻辑层。

分层模型

Figure 3

按照分层设计,我们假设Controller对应问题领域层,那么Controller将变成整个软件的核心层,Controller内部将包含业务实体和业务逻辑。

按照这个定位,那么Model就会成为职责单一的数据管理层,那么不同的 Model可以对不同的数据源进行管理,

例如,数据库数据源,本地文件数据源,网络数据源,以及不同源的组合源

被动MVC && 主动MVC

如果Model不承担通知视图更新的职责,由Controller负责通知更新,那么就是被动MVC模式

如果由Model承担通知视图更新的职责,是主动MVC模式

从实际操作,我倾向于被动MVC模式,这样可以提升View渲染的性能,而不需要经过Model层操作之后进行View渲染。

在实现中,需要注意数据一致性的问题。

例如信息流场景,回看历史数据的时候,如果之前Model持久出现异常,没有一直合适的机制进行处理,则会导致中间丢失部分数据,前后浏览的记录会不一致。

效果

按照上面的设计,会带来一些设计的效果

  • Controller成为软件的核心层,Controller可以通过组合View和Model,提供软件的灵活性和可配置性

figure [1]中的协作关系从软件设计上反映

MVC结构

Figure 4[被动MVC模式]

  1. Controller是一个策略,通过配置不同的Controller,对View的请求定制不同的响应
  2. View是一个观察者策略,和Controller进行解耦
  3. Controller也可以配置不同的Model,使用不同的数据管理
  4. 不同层有不同的数据抽象,使用不同的数据模型
  5. View不能直接访问Model,这样会破坏组件间的相互独立

MVC 本质上是通过分层的思想,使Controller,View,Model三个组件相互独立,支持View视图的灵活变化,以及Controller的配置改变数据层和业务规则。

Controller [假设]

业务逻辑层,负责业务逻辑的处理

Model [假设]

数据管理层,负责数据的获取

问题

按照上面的分析,以及各个组件的职责,的确可以达到重用Model,View的目的,同时Controller可以对不同的数据源,视图进行配置,灵活的满足客户需求。

但是这样Controller会显得过于庞大,我们实际只分离出了数据管理层,并没有对WndProc中各种业务处理进行分离,那是否我们可以配置多个Controller对应不同的M,这样Controller的就变成更简洁,职能更单一。

答案是应该这么做,并且可以提高Controller的复用性。

但是我们刚刚说到应用逻辑,监督和协调整个应用的活动。

如果Controller管理View以及Model之间的协作关系,同时Controller又具有业务的功能,那么对于Controller的职责来说是不单一的,因此我们需要重新定位Controller和Model的职责。

MVC职责修正

Figure 2 [修正]

那承担数据管理职责的层,对应数据的持久化的工作,则属于基础设施层,Model应该是包括业务逻辑的要素(领域实体,规则,约束),业务服务提供业务流程的协作管理。

MVC_修正

Figure 3[修正]

Controller

应用逻辑组件

连接视图和业务,控制程序的流程

Model

业务逻辑组件

用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法

Model层涉及的业务逻辑元素较多,其内部可以分离出数据访问层,用于抽象业务数据的获取,包括本地数据和远程数据的获取,目标是屏蔽掉数据访问的细节,例如数据库的连接,数据的存储过程细节,服务器接口协议以及解析过程。

核心

  • 关注点分离思想
  • 组件之间相互独立

MVP

通过MVC的分析,如果按照**Figure 4 **的交互关系,本质上和MVP是没有区别的只是把

Controller -> Presenter

Presenter的职责和Controller职责是完全一样的。

MVP是MVVM实施的一种特例。

MVP结构

Figure 5

MVC&& MVP区别

说到这里回到MVC 效果第5点

通常的谈到MVC和MVP的区别的时候,能否从View直接访问Model是一个区别的特征。

但是这个不是MVC必须的,很大的原因是因为MVC架构的框架实现导致这种理解。

因此我认为讨论View是否直接访问Model的优缺点更有意义。

View是否直接访问Model优缺点
  • 优点

    • View直接访问Model,缩短了链路的路由长度,在某些场景,会某些场景具有性能优势
    • View直接访问Model,不需要实现一套从View->Controller->Model->Controller->View的数据更新机制,实现比较简单,对于开发人员的要求较低,开发成本较低
  • 缺点

    • View直接依赖Model,虽然可以通过接口接口具体的Model类,从上下文多了一个外部的交互组件,

      关联的外部系统越多,就越容易造成自己系统的复杂性

    • View直接依赖Model,意味着View会直接和数据管理层发生联系,那意味着View将包含一部分业务逻辑,这对View的职责单一性产生了破坏,也导致了那部分业务逻辑无法重用

    • 对于Model来说,越多的外部调用者,破坏了代码的统一管理,在问题排查时增加了Model的维护成本

职责

View

职责上和MVC的View没有区别,对比MVC的View,没有给出明确的实现规范,MVP比较明确的提出了接口方式的抽象

Presenter

负责应用逻辑处理,和对视图的更新

Model

提供业务逻辑服务

Model需要提供对Presenter的数据更新没有具体的实现要求,可以通过回调,观察者去实现,但是Model需要实现相应的回传机制,把值变化返回给到Presenter,对应Model来说,Presenter是解耦的,保持Model的独立性

优点

  • 从架构思想上对比MVC,明确了视图层,业务层,数据层三大组件的通信方式,隔离了View和Model
  • 明确了通过View的接口抽象,实现Presenter的复用和View的独立性
  • 基于接口方式的代码,易于测试和方便调试,也易于理解

缺点

  • View的接口抽象,需要定义View的接口类,这样会引起View接口类和View具体类的一个耦合,具体体现就是视图发生变化,接口类需要修改,同时Presenter内也需要进行修改。

核心

  • View和Model隔离
  • View视图的抽象隔离

MVVM

介绍MVVM之前,我们先介绍一下

双向绑定

双向绑定是指视图和模型之前的动态互动关系,当模型发生变化时,视图能更新;同时当视图发生变化时候,模型也随之更新,这种双向的互动机制,就是双向绑定。

MVVM结构

Figure 6

职责

MVVM本质上和MVP也没有本质的区别,但是View和ViewModel之间使用了双向绑定,其实是为了解决MVP模式中需要手动同步Model->Presenter->View,View->Presenter->Model的手动代码编写,在MVVM架构风格提供的框架中自动的完成了。

View

对比MVP方式,通过视图绑定解耦了View的具体实现,同时通过数据的统一抽象框架,不需要再单独去编写View视图的对外接口,减少了开发的工作量。

同时基于数据抽象的方式,降低了业务对视图的敏感性。

ViewModel

提供了双向绑定的实现机制提供应用逻辑服务

Model

提供业务逻辑服务,Model通过可被观察的数据,直接实现了Model到ViewModel的数据返回,这样可以不用在Model内部维护一套数据更新的机制,通过统一的可观察的数据对象,达到数据的更新通知。

也就是交互图的Model->ViewModel的notify操作,不是必须的,如果使用可观察的对象,对可观察对象的修改,自动就会传递到视图层

优点

  • 改进了MVP需要抽象View的接口方式,而采用了数据抽象的方式,View的功能更加内聚
  • 同时采用数据绑定框架,可以避免MVP数据变化在各层顺序传递的流转,可以直接从业务传递到视图,代码看起来更简洁
  • 因为基于数据抽象,ViewModel没有直接和View产生接口调用的关系,而数据抽象是基于问题域的抽象,相对稳定很多,即使视图发生了很大的变化,ViewModel涉及的修改也很少甚至不修改,因此ViewModel的复用性和维护性更高
  • 基于上一点的优势,ViewModel很适合进行对不同的Model进行适配,而不需要修改View和Model,组件的独立性更好

缺点

  • MVVM方式实现比较复杂,实现上有比较高的开发成本,基本上需要利用第三方框架(例如data-binding框架),而第三方的框架的使用也具有一定的学习成本

  • MVVM通过更抽象的数据接口实现组件间的通信,这种方式的代码不利于代码和问题的跟踪排查

核心

  • 数据绑定,数据驱动,视图完全隔离
    • 可观察的数据对象
    • 视图直接绑定ViewModel中的值对象,当值发生变化时候,视图可以自动更新,同时支持双向数据绑定,视图变化修改ViewModel中的值对象

可参考官方提供的指导例子应用架构指南

总结

MVC,MVP,MVVM都是在MV*模式上的改进,本质上是一样,只是在组织的法则和指南上,即协作方式进行了不同的设计,以适用不同的场景,和减少开发人员的工作量。

同时也有相应的优缺点,新的改良都必须满足之前模式的核心内容。

因为本质上一样,基于场景来说,只能说通常开发使用,基于大部分人对MV*的理解来说

  • MVC适用了功能较少,业务逻辑比较简单的小项目
  • MVP考虑到接口化的交互方式,使用了业务比较复杂,但是界面比较简单的项目
  • MVVM适用于业务复杂,界面复杂的项目,通过数据驱动,消除了接口设计繁复的问题

共性

  • 明确模块的职责以及定义职责间的界限,可以通过[2]提供的指导方法进行思考
    • MVP明确了View和Model的隔离,本质上是更加明确了职责和职责间的边界,是高复用的基础
  • 识别影响软件质量中的因素(可复用性,可扩展性)
    • 视图的变化比模型的变化更加频繁,分离Model可以提升Model的复用性
    • 关注内聚的核心能力以及职责单一性,天然的能提升模块的可扩展性
  • 关注架构模式背后映射的设计模式,以及软件设计的一些方法原则(例如OOP原则),如何实现高内聚,低耦合,高复用的原理
  • 理解架构思想,并在实现上通过框架以及工具的手段去保证实施
    • Android的数据绑定的框架实现,以及视图生命周期的自动管理
    • 视图更新的实施过程中都会涉及到线程的问题,这块需要在业务层进行设计

不能解决的问题

上面说了使用架构模式通过关注点分离,实现了不同层的关注,但是架构模式本身解决不了子系统内部的问题,例如业务之间的抽象。

参考

[1] https://zh.wikipedia.org/zh/%E5%85%B3%E6%B3%A8%E7%82%B9%E5%88%86%E7%A6%BB 关注点分离

[2]http://aspiringcraftsman.com/2008/01/03/art-of-separation-of-concerns/ 关注点分离的艺术

[3]https://zh.wikipedia.org/wiki/MVC MVC模式

[4]https://docs.microsoft.com/zh-cn/cpp/windows/walkthrough-creating-windows-desktop-applications-cpp?view=vs-2019 演练:创建传统的 Windows 桌面应用程序

[5]《软件架构设计》第二版 温昱 第13章《如何分层》

[6]https://developer.android.com/jetpack/docs/guide 应用架构指南

[7] https://baike.baidu.com/item/架构模式/9283553?fr=aladdin 架构模式

[8]https://www.cnblogs.com/kaisadadi/p/9265127.html 细说业务逻辑

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容