python设计模式9模型视图控制器MVC(model-view-controller)

关注点分离(SoC separation of concerns)师软件工程相关的设计原则之一。SoC原则背后的想法是将应用程序分成不同的部分,每个部分解决一个单独的问题。这种关注点的例子是分层设计中使用的层(数据访问层、业务逻辑层、表现层等等)。使用SoC原则可以简化软件应用程序的开发和维护。

MVC模式只不过是将SoC原则应用于OOP。该模式的名称来自于用于分割软件应用的三个主要组件:模型、视图和控制器。MVC被认为是一种架构模式,而不是一种设计模式。架构模式和设计模式的区别在于,前者比后者的范围更广。然而,MVC太重要了,不能因为这个原因就跳过。即使我们永远不需要从头开始实现它,我们也需要熟悉它,因为所有常见的框架都使用MVC或其变种。

模型是核心组件。它代表知识。它包含并管理应用程序的(业务)逻辑、数据、状态和规则。视图是模型的可视化表示。视图的例子有计算机GUI、计算机终端的文本输出、智能手机的应用GUI、PDF文档、饼状图、柱状图等等。视图只显示数据,它并不处理数据。控制器是模型和视图之间的纽带/粘合剂。模型和视图之间的所有通信都通过控制器进行。

典型的使用MVC的应用程序,在初始屏幕呈现给用户后,其使用情况如下。

  • 用户通过点击(输入、触摸等)按钮来触发一个视图。
  • 视图将用户的操作告知控制器
  • 控制器处理用户输入并与模型进行交互
  • 模型执行所有必要的验证和状态变化,并通知控制器应该做什么。
  • 控制器指示视图按照模型给出的指示,适当地更新和显示输出。

你可能想知道,为什么控制器部分是必要的?我们就不能跳过它吗?可以,但这样我们就会失去很大的好处:可以在不修改模型的情况下使用多个视图(甚至同时使用,如果我们想这样的话)。为了实现模型和其表现形式之间的解耦,每个视图通常都需要自己的控制器。如果模型直接与特定的视图通信,我们就不能使用多个视图(或者至少,不能以干净和模块化的方式)。

真实世界的例子

MVC是应用于OOP的SoC原则。SoC原则在现实生活中被大量使用。例如,如果你建造一栋新房子,你通常会指派不同的专业人员去做。1)安装水管和电;以及,2)喷漆。

在餐馆里,服务员接受订单并为顾客提供菜肴,但饭菜是由厨师烹制的。

在Web开发中,有几个框架采用了MVC的思想。

  • Web2py框架MVC模式的轻量级Python框架。
  • Django也是一个MVC框架,尽管它使用不同的命名规则。控制器被称为视图,而视图被称为模板。Django使用的是Model-Template-View(MTV)这个名字。根据Django的设计者,视图描述了用户看到的数据是什么,因此,它使用视图这个名字作为特定URL的Python回调函数。Django中的模板一词是用来将内容和表现形式分开的。它描述了用户是如何看到数据的,而不是看到哪些数据。

应用

MVC是一个非常通用和有用的设计模式。事实上,所有流行的Web框架(Django、Rails和Symfony或Yii)和应用程序框架(iPhone SDK、Android和QT)都使用了MVC或其变体--模型-视图-适配器(MVA)、模型-视图-呈现器(MVP),等等。然而,即使我们不使用这些框架,在我们自己身上实现该模式也是有意义的,因为它提供了以下好处。

  • 视图和模型之间的分离使得图形设计师可以专注于UI部分,程序员可以专注于开发,而不会相互干扰。
  • 由于视图和模型之间的松散耦合,每个部分都可以被修改/扩展而不影响其他部分。例如,添加一个新的视图是微不足道的。只要为它实现一个新的控制器。
  • 每个部分的维护都很容易,因为责任很明确。

当从头开始实现MVC时,要确保你创建了智能模型、瘦控制器和傻瓜视图。

模型被认为是智能的,因为:

  • 包含所有的验证/业务规则/逻辑
  • 处理应用程序的状态
  • 可以访问应用程序的数据(数据库、云等)。
  • 不依赖于UI

控制器被认为是瘦的,因为。

  • 当用户与视图交互时更新模型
  • 当模型发生变化时更新视图
  • 如有必要,在向模型/视图传递数据之前对其进行处理
  • 不显示数据
  • 不直接访问应用程序的数据
  • 不包含验证/业务规则/逻辑

视图被认为是傻瓜的,因为:

  • 显示数据
  • 允许用户与之交互
  • 只做最小的处理,通常由模板语言提供(例如,使用简单的变量和循环控制)。
  • 不存储任何数据
  • 不直接访问应用程序的数据
  • 不包含验证/业务规则/逻辑

如果你正在从头开始实施MVC,并想知道你是否做对了,你可以试着回答一些关键问题。

  • 应用程序的GUI是否可以换肤?你能多容易地改变它的皮肤/外观和感觉?你能让用户在运行时改变你的应用程序的皮肤吗?如果这并不简单,那就意味着你的MVC实现出了问题。

  • 如果你的应用程序没有GUI(例如,如果它是一个终端应用程序),添加GUI支持有多难?或者,如果添加GUI无关紧要,那么添加视图以在图表(饼图、柱状图等)或文档(PDF、电子表格等)中显示结果是否容易?如果这些变化不是很琐碎(只是创建一个新的控制器,并在其上附加一个视图,而不修改模型),那么MVC就没有正确实现。

如果你确保这些条件得到满足,与不使用MVC的应用程序相比,你的应用程序将更加灵活和可维护。

实例

我们用非常简单的例子向你展示如何从头开始实现MVC:报价打印机。用户输入一个数字,看到与该数字相关的报价。报价被存储在元组中,实际应用多存在数据库、文件等,只有模型可以直接访问它。

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(f'And the quote is: "{quote}"') 
 
    def error(self, msg): 
        print(f'Error: {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) 
                valid_input = True 
            except ValueError as err: 
                self.view.error(f"Incorrect index '{n}'") 
        quote = self.model.get_quote(n) 
        self.view.show(quote) 
 
def main(): 
    controller = QuoteTerminalController() 
    while True: 
        controller.run() 
 
if __name__ == '__main__': 
    main()

这个模型只有一个 get_quote() 方法,根据它的索引 n 返回 quotes 元组的报价 (字符串)。

视图有三个方法:show(),用来在屏幕上打印一个引用(或消息Not found!),error(),用来在屏幕上打印一个错误信息,select_quote(),读取用户的选择。这可以从下面的代码中看出。

控制器做协调工作。init()方法初始化模型和视图。run()方法验证用户给出的报价指数,从模型中获得报价,并将其传回给视图显示,如下代码所示。

最后但同样重要的是,main()函数初始化并启动控制器。

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

推荐阅读更多精彩内容