python设计模式之模型-视图-控制器模式

关注点分离( Separation of Concerns, SoC)原则是软件工程相关的设计原则之一。 SoC原则背后的思想是将一个应用切分成不同的部分,每个部分解决一个单独的关注点。分层设计中的层次(数据访问层、业务逻辑层和表示层等)即是关注点的例子。使用SoC原则能简化软件应用的开发和维护。

模型—视图—控制器( Model-View-Controller, MVC)模式是应用到面向对象编程的Soc原则。模式的名称来自用来切分软件应用的三个主要部分,即模型部分、视图部分和控制器部分。 MVC被认为是一种架构模式而不是一种设计模式。架构模式与设计模式之间的区别在于前者比后者的范畴更广。然而, MVC太重要了,不能仅因为这个原因就跳过不说。即使我们从不需要从头实现它,也需要熟悉它,因为所有常见框架都使用了MVC或者是其略微不同的版本。

模型是核心的部分,代表着应用的信息本源,包含和管理(业务)逻辑、数据、状态以及应用的规则。视图是模型的可视化表现。视图的例子有,计算机图形用户界面、计算机终端的文本输出、智能手机的应用图形界面、 PDF文档、饼图和柱状图等。视图只是展示数据,并不处理数据。控制器是模型与视图之间的链接/粘附。模型与视图之间的所有通信都通过控制器进行。

对于将初始屏幕渲染给用户之后使用MVC的应用,其典型使用方式如下所示。

  • [ ] 用户通过单击(键入、触摸等)某个按钮触发一个视图
  • [ ] 视图把用户操作告知控制器
  • [ ] 控制器处理用户输入,并与模型交互
  • [ ] 模型执行所有必要的校验和状态改变,并通知控制器应该做什么
  • [ ] 控制器按照模型给出的指令,指导视图适当地更新和显示输出

你可能想知道为什么控制器部分是必要的?我们能跳过它吗?能,但那样我们将失去MVC提供的一大优势:无需修改模型就能使用多个视图的能力(甚至可以根据需要同时使用多个视图)。为了实现模型与其表现之间的解耦,每个视图通常都需要属于它的控制器。如果模型直接与特定视图通信,我们将无法对同一个模型使用多个视图(或者至少无法以简洁模块化的方式实现)。

1. 现实生活的例子

MVC是应用于面向对象编程的SoC原则。 SoC原则在现实生活中的应用有很多。例如,如果你造一栋新房子,通常会请不同的专业人员来完成以下工作。

  • [ ] 安装管道和电路
  • [ ] 粉刷房子

另一个例子是餐馆。在一个餐馆中,服务员接收点菜单并为顾客上菜,但是饭菜由厨师烹饪。

2. 软件的例子

Django是一个MVC框架,但是它使用了不同的命名约定。在此约定下,控制器被称为视图,视图被称为模板。 Django使用名称模型—模板—视图( Model-Template-View, MTV)来替代。依据Django的设计者所言,视图是描述哪些数据对用户可见。因此, Django把对应一个特定URL的Python回调函数称为视图。 Django中的“模板”用于把内容与其展现分开,其描述的是用户看到数据的方式,而不是哪些数据可见

3. 应用案例

MVC是一个非常通用且大有用处的设计模式。实际上,所有流行的Web框架( Django、 Rails和Yii)和应用框架( iPhone SDK、 Android和QT)都使用了MVC或者其变种,其变种包括模式—视图—适配器( Model-View-Adapter, MVA)、 模型—视图—演示者( Model-View-Presenter,MVP)等。然而,即使我们不使用这些框架,凭自己实现这一模式也是有意义的,因为这一模式提供了以下这些好处。

  • [ ] 视图与模型的分离允许美工一心搞UI部分,程序员一心搞开发,不会相互干扰。
  • [ ] 由于视图与模型之间的松耦合,每个部分可以单独修改/扩展,不会相互影响。例如,添

    加一个新视图的成本很小,只要为其实现一个控制器就可以了。
  • [ ] 因为职责明晰,维护每个部分也更简单.

在从头开始实现MVC时,请确保创建的模型很智能,控制器很瘦,视图很傻瓜。

可以将具有以下功能的模型视为智能模型。

  • [ ] 包含所有的校验/业务规则/逻辑
  • [ ] 处理应用的状态
  • [ ] 访问应用数据(数据库、云或其他)
  • [ ] 不依赖UI

可以将符合以下条件的控制器视为瘦控制器。

  • [ ] 在用户与视图交互时,更新模型
  • [ ] 在模型改变时,更新视图
  • [ ] 如果需要,在数据传递给模型/视图之前进行处理
  • [ ] 不展示数据
  • [ ] 不直接访问应用数据
  • [ ] 不包含校验/业务规则/逻辑

可以将符合以下条件的视图视为傻瓜视图。

  • [ ] 展示数据
  • [ ] 允许用户与其交互
  • [ ] 仅做最小的数据处理,通常由一种模板语言提供处理能力(例如,使用简单的变量和循

    环控制)
  • [ ] 不存储任何数据
  • [ ] 不直接访问应用数据
  • [ ] 不包含校验/业务规则/逻辑

如果你正在从头实现MVC,并且想弄清自己做得对不对,可以尝试回答以下两个关键问题。

  • [ ] 如果你的应用有GUI,那它可以换肤吗?易于改变它的皮肤/外观以及给人的感受吗?可

    以为用户提供运行期间改变应用皮肤的能力吗?如果这做起来并不简单,那就意味着你

    的MVC实现在某些地方存在问题。
  • [ ] 如果你的应用没有GUI(例如,是一个终端应用),为其添加GUI支持有多难?或者,如

    果添加GUI没什么用,那么是否易于添加视图从而以图表(饼图、柱状图等)或文档( PDF、

    电子表格等)形式展示结果?如果因此而作出的变更不小(小的变更是,在不变更模型

    的情况下,创建控制器并绑定到视图),那你的MVC实现就有些不对了。

如果你确信这两个条件都已满足,那么与未使用MVC模式的应用相比,你的应用会更灵活、更好维护。

4. 实现

我可以使用任意常见框架来演示如何使用MVC,但觉得那样的话,读者对MVC的理解会不完整。因此我决定使用一个非常简单的示例来展示如何从头实现MVC,这个示例是名人名言打印机。想法极其简单:用户输入一个数字,然后就能看到与这个数字相关的名人名言。名人名言存储在一quotes元组中。这种数据通常是存储在数据库、文件或其他地方,只有模型能够直接访问它。

我们从下面的代码开始考虑这个例子。

quotes = ('A man is not complete until he is married. Then he is finished.',
'As I said before, I never repeat myself.','Behind a successful man is an exhausted woman.','Black holes really suck...', 'Facts are stubborn things.')

模型极为简约,只有一个get_quote()方法,基于索引n从quotes元组中返回对应的名人名言(字符串)。注意, n可以小于等于0,因为这种索引方式在Python中是有效的。

class QuoteModel:
def get_quote(self, n):
try:
value = quotes[n]
except IndexError as err:
value = 'Not found!'
return value

视图有三个方法,分别是show()、 error()和select_quote()。 show()用于在屏幕上输出一句名人名言(或者输出提示信息Not found!); error()用于在屏幕上输出一条错误消息;select_quote()用于读取用户的选择,如以下代码所示。

class QuoteTerminalView:
def show(self, quote):
print('And the quote is: "{}"'.format(quote))
def error(self, msg):
print('Error: {}'.format(msg))
def select_quote(self):
return input('Which quote number would you like to see? ')

控制器负责协调。 init()方法初始化模型和视图。 run()方法校验用户提供的名言索

引,然后从模型中获取名言,并返回给视图展示,如以下代码所示。

class QuoteTerminalController:
def __init__(self):
self.model = QuoteModel()
self.view = QuoteTerminalView()
def run(self):
valid_input = False
while not valid_input:
n = self.view.select_quote()
try:
n = int(n)
except ValueError as err:
self.view.error("Incorrect index '{}'".format(n))
else:
valid_input = True
quote = self.model.get_quote(n)
self.view.show(quote)

最后,但同样重要的是, main()函数初始化并触发控制器,如以下代码所示。

def main():
controller = QuoteTerminalController()
while True:
controller.run()

以下是该示例的完整代码:

quotes = ('A man is not complete until he is married. Then he is finished.',
'As I said before, I never repeat myself.','Behind a successful man is an exhausted woman.','Black holes really suck...', 'Facts are stubborn things.')
class QuoteModel:
def get_quote(self, n):
try:
value = quotes[n]
except IndexError as err:
value = 'Not found!'
return value
class QuoteTerminalView:
def show(self, quote):
print('And the quote is: "{}"'.format(quote))
def error(self, msg):
print('Error: {}'.format(msg))
def select_quote(self):
return input('Which quote number would you like to see? ')
class QuoteTerminalController:
def __init__(self):
self.model = QuoteModel()
self.view = QuoteTerminalView()
def run(self):
valid_input = False
while not valid_input:
try:
n = self.view.select_quote()
n = int(n)
vaild_input = True
except ValueError as err:
self.view.error("Incorrect index '{}'".format(n))
quote = self.model.get_quote(n)
self.view.show(quote)
def main():
controller = QuoteTerminalController()
while True:
controller.run()
if __name__ == '__main__':
main()

输出:

Which quote number would you like to see? a
Error: Incorrect index 'a'
Which quote number would you like to see? 40
And the quote is: "Not found!"
Which quote number would you like to see? 0
And the quote is: "A man is not complete until he is married. Then he is
finished."
Which quote number would you like to see? 3
And the quote is: "Black holes really suck..."
5. 小结

MVC是一个非常重要的设计模式,用于将应用组织成三个部分:模型、视图和控制器。

每个部分都有明确的职责。模型负责访问数据,管理应用的状态。视图是模型的外在表现。视图并非必须是图形化的;文本输出也是一种好视图。控制器是模型与视图之间的连接。 MVC的恰当使用能确保最终产出的应用易于维护、易于扩展。

MVC模式是应用到面向对象编程的SoC原则。这一原则类似于一栋新房子如何建造,或一个餐馆如何运营。

使用MVC时,请确保创建智能的模型(核心功能)、瘦控制器(实现视图与模型之间通信的能力)以及傻瓜式的视图(外在表现,最小化逻辑处理)。

python设计模式之模型-视图-控制器模式的更多相关文章

  1. 模型-视图-控制器模式(MVC模式,10种常见体系架构模式之一)

    一.简介: 架构模式是一个通用的.可重用的解决方案,用于在给定上下文中的软件体系结构中经常出现的问题.架构模式与软件设计模式类似,但具有更广泛的范围. 模型-视图-控制器模式,也称为MVC模式.是软件 ...

  2. 设计模式-结构型模式, mvc 模型视图控制器模式(8)

    MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式.这种模式用于应用程序的分层开发. Model(模型) - 模型代表一个存取数据的对象或 JAVA POJO.它 ...

  3. [框架模式]经典的模型视图控制器模式MVC

    参考:<设计模式> http://blog.csdn.net/u010168160/article/details/43150049 百度百科 引言: Model(模型)是应用程序中用于处 ...

  4. 设计模式 --- 模型-视图-控制器(Model View Controller)

    模型-视图-控制器(Model-View-Controller,MVC)是Xerox PARC在20世纪80年代为编程语言Smalltalk-80发明的一种软件设计模式,至今已广泛应用于用户交互应用程 ...

  5. 模型 - 视图 - 控制器(MVC)详解

    模型视图控制器(MVC)一个相当实用且十分流行的设计模式.作为一位称职码农,你不可能没听说过吧. 不幸的是它难以让人理解. 在本文中,我将给出我认为是MVC的最简单的解释,以及为什么你应该使用它. 什 ...

  6. MODEL-View-Controller,既模型-视图-控制器

    Swing组件采用MVC(MODEL-View-Controller,既模型-视图-控制器)设计模式,其中模型(Model)用于维护组件的各种状态,视图(View)是组件的可视化表现,控制器(Cont ...

  7. 模型-视图-控制器的C++解释

    模型-视图-控制器 (MVC) 并非一种技术,而是软件设计/工程的一个概念.MVC包含三个组成部分,如下图所示 模型 模型直接响应对数据的处理,比如数据库.模型不应依赖其它组成部分,即视图或控制器,换 ...

  8. 学习模型-视图-控制器MVC模式

    1.MVC简介 MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑.数据.界面显示分 ...

  9. MVC模式 - Model-View-Controller -(模型-视图-控制器)

    MVC(Model View Controller) MVC是一种设计典范.它是用一种业务逻辑.数据与界面显示分离的方法来组织代码,将众多的业务逻辑聚集到一个部件上,在需要改进和个性化定制界面及用户交 ...

随机推荐

  1. React Native 适配Android物理返回键,实现连续两次点击退出

    一直使用iPhone作为测试机开发,提交给测试同事Android版本后发现很多适配问题,其中一个非常明显的是,弹出一个modal后,点击Android的返回键,modal不会消失,直接navigati ...

  2. 线上CUP负载过高排查方法

      1.top命令查看线程占据的CPU 注意:上面行的cpu是多个内核的平均CPU,不可能超过100% 下面的cpu是每个进程实际占用的cpu,可能超过100% 备注:查看多个内核cpu,只需要在输入 ...

  3. 快速配置webpack+vue项目

    第一步:初始化项目 1.npm init 2. package name: (webpack+vue) webpackvue version: (1.0.0) description: this is ...

  4. SpringBoot-使用lombok插件运行报错

    SpringBoot-使用lombok插件运行报错 标签(空格分隔): java,SpringBoot 1.报错信息 2.解决方案 1.IDEA编辑器安装lombok插件 2.编译注解配置-Enabl ...

  5. Python编程语言简介

    Python诞生于20世纪90年代初,由荷兰人吉多·范罗苏姆发明.那么,Python这一门编程语言是如何发明的呢?这之中又有怎么的故事呢?请看下面. 故事发生在1989年的圣诞节上,吉多先生为了打发无 ...

  6. Numpy数组排序

    import numpy as np x = np.array([1,4,5,2]) # array([1, 4, 5, 2]) # 返回排序后元素的原下标 np.argsort(x) # array ...

  7. PHP filegroup() 函数

    定义和用法 filegroup() 函数返回指定文件的组 ID. 如果成功,该函数返回指定文件所属组的 ID.如果失败,则返回 FALSE. 语法 filegroup(filename) 参数 描述 ...

  8. PHP fgetcsv() 函数

    定义和用法 fgetcsv() 函数从打开的文件中解析一行,校验 CSV 字段. fgetcsv() 函数会在到达指定长度或读到文件末尾(EOF)时(以先到者为准),停止返回一个新行. 该函数如果成功 ...

  9. PHP zip_close() 函数

    定义和用法 The zip_close() 函数关闭由 zip_open() 函数打开的 zip 档案.高佣联盟 www.cgewang.com 语法 zip_close(zip) 参数 描述 zip ...

  10. luogu P5289 [十二省联考2019]皮配 背包

    LINK:皮配 我承认是一道很难的题目. 不过对于这道题 部分分的提示显得尤为重要. 首先是 40分的暴力dp 很容易想 但是不容易写. 从40分可以发现我们只需要把蓝阵营和鸭派系的人数给存在起来就行 ...