python设计模式之模型-视图-控制器模式
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设计模式之模型-视图-控制器模式的更多相关文章
- 模型-视图-控制器模式(MVC模式,10种常见体系架构模式之一)
一.简介: 架构模式是一个通用的.可重用的解决方案,用于在给定上下文中的软件体系结构中经常出现的问题.架构模式与软件设计模式类似,但具有更广泛的范围. 模型-视图-控制器模式,也称为MVC模式.是软件 ...
- 设计模式-结构型模式, mvc 模型视图控制器模式(8)
MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式.这种模式用于应用程序的分层开发. Model(模型) - 模型代表一个存取数据的对象或 JAVA POJO.它 ...
- [框架模式]经典的模型视图控制器模式MVC
参考:<设计模式> http://blog.csdn.net/u010168160/article/details/43150049 百度百科 引言: Model(模型)是应用程序中用于处 ...
- 设计模式 --- 模型-视图-控制器(Model View Controller)
模型-视图-控制器(Model-View-Controller,MVC)是Xerox PARC在20世纪80年代为编程语言Smalltalk-80发明的一种软件设计模式,至今已广泛应用于用户交互应用程 ...
- 模型 - 视图 - 控制器(MVC)详解
模型视图控制器(MVC)一个相当实用且十分流行的设计模式.作为一位称职码农,你不可能没听说过吧. 不幸的是它难以让人理解. 在本文中,我将给出我认为是MVC的最简单的解释,以及为什么你应该使用它. 什 ...
- MODEL-View-Controller,既模型-视图-控制器
Swing组件采用MVC(MODEL-View-Controller,既模型-视图-控制器)设计模式,其中模型(Model)用于维护组件的各种状态,视图(View)是组件的可视化表现,控制器(Cont ...
- 模型-视图-控制器的C++解释
模型-视图-控制器 (MVC) 并非一种技术,而是软件设计/工程的一个概念.MVC包含三个组成部分,如下图所示 模型 模型直接响应对数据的处理,比如数据库.模型不应依赖其它组成部分,即视图或控制器,换 ...
- 学习模型-视图-控制器MVC模式
1.MVC简介 MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑.数据.界面显示分 ...
- MVC模式 - Model-View-Controller -(模型-视图-控制器)
MVC(Model View Controller) MVC是一种设计典范.它是用一种业务逻辑.数据与界面显示分离的方法来组织代码,将众多的业务逻辑聚集到一个部件上,在需要改进和个性化定制界面及用户交 ...
随机推荐
- xss利用
xss盗取cookie 什么是cookie cookie是曲奇饼,啊开个玩笑,cookie是每个用户登录唯一id和账号密码一样可以登录到网站,是的你没有听错cookie可以直接登录,至于服务器怎么设置 ...
- p41_数据报与虚电路
一.定义 数据报方式为网络层提供无连接服务. 无连接服务:不事先为分组的传输确定传输路径,每个分组独立确定传输路径,不同分组传输路径可能不同. 虚电路方式为网络层提供连接服务 连接服务:首先为分组的传 ...
- Python基础知识点:多进程的应用讲解
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:东哥IT笔记 现在很多CPU都支持多核,甚至是手机都已经开始支持多核 ...
- 七牛云如何绑定二次验证码_虚拟MFA_两步验证_谷歌身份验证器?
一般情况下,点账户名——账户设置——安全设置,即可开通两步验证 具体步骤见链接 七牛云如何绑定二次验证码_虚拟MFA_两步验证_谷歌身份验证器? 二次验证码小程序(官网)对比谷歌身份验证器APP ...
- Spring Boot+MyBatis+MySQL读写分离
读写分离要做的事情就是对于一条sql语句该选择去哪个数据库执行,至于谁来做选择数据库的事情,无非两个,1:中间件(比如MyCat):二:程序自己去做分离操作. 但是从程序成眠去做读写分离最大的弱点就是 ...
- 大家是怎么做APP接口的版本控制的?欢迎进来看看我的方案。升级版的Versioning
背景 APP不同于网站,网站程序一发版,所有用户看到的都是最新的页面.调用最新的接口,没有新老版本一说.APP一旦下载到用户手机上,用户不更新你拿他一点办法都没有,但是随着业务的调整,同一个接口的请求 ...
- Redis之Redis的数据类型
1.Redis的数据类型 Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(无序集合)及ZSet(有序集合) 2.String(字符串) ...
- 字符输入输出不一样!:什么情况下需要getchar()吃空格和回车
今天一个很简单的题居然一直不对... 大概是用字符组成的一个方块..然后各种转换, 关键是我算法都写好了,然而输入进去的字符方块直接输出来都不太对... 后来想起吃空格问题,因为scanf了%c的话, ...
- Python os.tcgetpgrp() 方法
概述 os.tcgetpgrp() 方法用于回与终端fd(一个由os.open()返回的打开的文件描述符)关联的进程组.高佣联盟 www.cgewang.com 语法 tcgetpgrp()方法语法格 ...
- PHP fstat() 函数
定义和用法 fstat() 函数返回关于一个打开的文件的信息. 该函数将返回一个包含下列元素的数组: [0] 或 [dev] - 设备编号 [1] 或 [ino] - inode 编号 [2] 或 [ ...