Python高级技术之:探讨`Python`的`MVC`、`MVP`和`MVVM`架构模式在`Web`框架中的应用。

嘿,各位朋友,晚上好!欢迎来到今晚的“架构那些事儿”小讲堂。今天咱们不聊别的,就扒一扒 Python Web 框架里那些个“M”打头的家伙:MVC、MVP 和 MVVM。保证让你听完之后,感觉自己瞬间升了一个段位!

开场白:架构,不止是盖房子

咱们写代码,就像盖房子。一开始搭个小棚子,随便怎么来都行。但房子大了,就得好好设计,不然住着不舒服,还容易塌。架构模式就是盖房子的设计图,它能让你的代码更清晰、更容易维护、扩展也更方便。

第一部分:MVC (Model-View-Controller)

MVC,全称 Model-View-Controller,翻译过来就是“模型-视图-控制器”。这三位是铁三角,关系很密切。

  • Model(模型): 负责处理数据。你可以把它看作是数据库的代理,或者直接就是你的数据对象。它负责数据的获取、存储、更新等操作。

  • View(视图): 负责展示数据。它就是用户看到的界面,比如 HTML 页面。

  • Controller(控制器): 负责接收用户的请求,调用 Model 处理数据,然后选择合适的 View 展示给用户。它就像一个交通警察,指挥着 Model 和 View 之间的交互。

MVC 的工作流程:

  1. 用户发起请求 (比如点击一个按钮)。
  2. Controller 接收到请求。
  3. Controller 调用 Model 去获取或更新数据。
  4. Model 完成数据操作。
  5. Controller 选择合适的 View 并将数据传递给 View。
  6. View 将数据渲染成 HTML 页面展示给用户。

代码示例 (简化版的 Flask MVC):

首先,安装 Flask:

pip install Flask

然后,创建一个简单的 MVC 应用:

# model.py
class User:
    def __init__(self, id, name, email):
        self.id = id
        self.name = name
        self.email = email

    def get_user(self, user_id):
        # 模拟从数据库获取用户
        if user_id == 1:
            return User(1, "Alice", "[email protected]")
        else:
            return None

# controller.py
from flask import Flask, render_template
from model import User

app = Flask(__name__)

@app.route("/user/<int:user_id>")
def user_profile(user_id):
    user = User(None, None, None).get_user(user_id)  #实例化对象,然后调用方法
    if user:
        return render_template("user_profile.html", user=user)
    else:
        return "User not found"

# view: user_profile.html (放在 templates 文件夹下)
# 注意要创建templates文件夹

# model = user
# model.name = user.name
# model.email = user.email

"""
<!DOCTYPE html>
<html>
<head>
    <title>User Profile</title>
</head>
<body>
    <h1>User Profile</h1>
    <p>ID: {{ user.id }}</p>
    <p>Name: {{ user.name }}</p>
    <p>Email: {{ user.email }}</p>
</body>
</html>
"""

if __name__ == "__main__":
    app.run(debug=True)

代码解释:

  • model.py: 定义了一个 User 类,模拟了从数据库获取用户数据的过程。
  • controller.py: 使用 Flask 创建了一个 Web 应用,user_profile 函数就是一个 Controller,它接收 user_id 参数,调用 Model 获取用户数据,然后将数据传递给 user_profile.html 模板进行渲染。
  • user_profile.html: 是一个简单的 HTML 模板,它使用 Flask 的模板引擎来展示用户数据。

MVC 的优点:

  • 职责分离: Model、View 和 Controller 各司其职,代码结构清晰。
  • 可维护性高: 修改 Model 不会影响 View,修改 View 不会影响 Model,易于维护。
  • 可测试性好: 可以单独测试 Model、View 和 Controller。

MVC 的缺点:

  • Controller 太重: Controller 承担了太多的职责,容易变得臃肿。
  • View 和 Model 耦合: 在简单的应用中,View 可能会直接访问 Model,造成耦合。

常见的 Python MVC 框架:

  • Django: 一个重量级的 Web 框架,自带 ORM (Object-Relational Mapping) 和模板引擎。
  • Flask: 一个轻量级的 Web 框架,灵活易用,适合小型项目。
  • Pyramid: 一个中等规模的 Web 框架,介于 Django 和 Flask 之间。

第二部分:MVP (Model-View-Presenter)

MVP,全称 Model-View-Presenter,翻译过来就是“模型-视图-展示器”。 MVP 是 MVC 的一种变体,主要解决 MVC 中 Controller 太重的问题。

  • Model(模型): 和 MVC 中的 Model 一样,负责处理数据。

  • View(视图): 和 MVC 中的 View 一样,负责展示数据。但是,MVP 中的 View 更加被动,它只负责展示数据,不负责处理用户交互。

  • Presenter(展示器): 负责接收用户的请求,调用 Model 处理数据,然后更新 View。它是 View 和 Model 之间的桥梁。

MVP 的工作流程:

  1. 用户在 View 上发起操作 (比如点击一个按钮)。
  2. View 将操作传递给 Presenter。
  3. Presenter 调用 Model 去获取或更新数据。
  4. Model 完成数据操作。
  5. Presenter 更新 View。
  6. View 展示更新后的数据。

MVP 的关键:View 不直接和 Model 交互,所有的交互都通过 Presenter。

代码示例 (简化版的 Python MVP):

# model.py
class User:
    def __init__(self, id, name, email):
        self.id = id
        self.name = name
        self.email = email

    def get_user(self, user_id):
        # 模拟从数据库获取用户
        if user_id == 1:
            return User(1, "Alice", "[email protected]")
        else:
            return None

# view.py
class UserView:
    def __init__(self, presenter):
        self.presenter = presenter

    def display_user(self, user):
        if user:
            print(f"ID: {user.id}")
            print(f"Name: {user.name}")
            print(f"Email: {user.email}")
        else:
            print("User not found")

    def on_get_user_button_clicked(self, user_id):
        self.presenter.get_user(user_id)

# presenter.py
class UserPresenter:
    def __init__(self, view, model):
        self.view = view
        self.model = model

    def get_user(self, user_id):
        user = self.model.get_user(user_id)
        self.view.display_user(user)

# main.py
from model import User
from view import UserView
from presenter import UserPresenter

model = User(None, None, None)
view = UserView(None)
presenter = UserPresenter(view, model)
view.presenter = presenter  # 设置 presenter,让 view 可以调用 presenter 的方法

view.on_get_user_button_clicked(1)  # 模拟用户点击按钮

代码解释:

  • model.py: 和 MVC 中的 Model 一样,负责获取用户数据。
  • view.py: UserView 类负责展示用户数据,它有一个 on_get_user_button_clicked 方法,模拟用户点击按钮,将用户 ID 传递给 Presenter。
  • presenter.py: UserPresenter 类负责接收 View 的请求,调用 Model 获取数据,然后更新 View。
  • main.py: 创建 Model、View 和 Presenter 的实例,并将它们连接起来。

MVP 的优点:

  • 职责更分离: View 只负责展示数据,Presenter 负责处理用户交互和数据更新,职责更加清晰。
  • 可测试性更好: 可以单独测试 View 和 Presenter,更容易进行单元测试。
  • View 更易于替换: 由于 View 不直接和 Model 交互,可以更容易地替换 View 的实现。

MVP 的缺点:

  • 代码量增加: 需要编写 Presenter 类,代码量比 MVC 略有增加。
  • Presenter 维护成本: Presenter需要维护View的状态,相对比较复杂。

常见的 Python MVP 应用场景:

  • GUI 应用: 例如使用 PyQt 或 Tkinter 开发的桌面应用。
  • 复杂的 Web 应用: 例如使用 React、Vue 或 Angular 开发的前端应用。

第三部分:MVVM (Model-View-ViewModel)

MVVM,全称 Model-View-ViewModel,翻译过来就是“模型-视图-视图模型”。 MVVM 是 MVP 的一种改进,主要解决 MVP 中 View 和 Presenter 之间耦合的问题。

  • Model(模型): 和 MVC 和 MVP 中的 Model 一样,负责处理数据。

  • View(视图): 和 MVP 中的 View 类似,负责展示数据。但是,MVVM 中的 View 可以通过数据绑定直接和 ViewModel 交互。

  • ViewModel(视图模型): 负责将 Model 中的数据转换成 View 可以直接使用的格式,并暴露一些命令供 View 调用。ViewModel 相当于 View 的数据源和命令源。

MVVM 的工作流程:

  1. View 通过数据绑定监听 ViewModel 中的数据变化。
  2. 用户在 View 上发起操作 (比如点击一个按钮)。
  3. View 调用 ViewModel 中的命令。
  4. ViewModel 调用 Model 去获取或更新数据。
  5. Model 完成数据操作。
  6. ViewModel 更新自身的数据。
  7. View 自动更新 (由于数据绑定)。

MVVM 的关键:数据绑定。View 和 ViewModel 之间通过数据绑定进行交互,ViewModel 的数据变化会自动反映到 View 上,反之亦然。

代码示例 (简化版的 Python MVVM):

# model.py
class User:
    def __init__(self, id, name, email):
        self.id = id
        self.name = name
        self.email = email

    def get_user(self, user_id):
        # 模拟从数据库获取用户
        if user_id == 1:
            return User(1, "Alice", "[email protected]")
        else:
            return None

# view_model.py
class UserViewModel:
    def __init__(self, model):
        self.model = model
        self._user_name = "" # 设置为私有属性
        self._user_email = ""

    @property
    def user_name(self):
        return self._user_name

    @user_name.setter
    def user_name(self, value):
        self._user_name = value
        # 在这里可以触发数据更新的事件,例如通知 View 更新 UI
        print(f"User name changed to: {value}") # 可以替换成事件触发机制

    @property
    def user_email(self):
        return self._user_email

    @user_email.setter
    def user_email(self, value):
        self._user_email = value
        print(f"User email changed to: {value}") # 可以替换成事件触发机制

    def load_user(self, user_id):
        user = self.model.get_user(user_id)
        if user:
            self.user_name = user.name
            self.user_email = user.email
        else:
            self.user_name = "User not found"
            self.user_email = ""

# view.py (简化版,实际应用需要使用 GUI 框架)
class UserView:
    def __init__(self, view_model):
        self.view_model = view_model

    def display(self):
        print(f"Name: {self.view_model.user_name}")
        print(f"Email: {self.view_model.user_email}")

    def on_load_button_clicked(self, user_id):
        self.view_model.load_user(user_id)
        self.display() # 重新渲染

# main.py
from model import User
from view_model import UserViewModel
from view import UserView

model = User(None, None, None)
view_model = UserViewModel(model)
view = UserView(view_model)

view.on_load_button_clicked(1)

view_model.user_name = "Bob" # 更新 ViewModel 的数据,View 会自动更新(在这个简化版中,需要手动调用 display())
view.display()

代码解释:

  • model.py: 和之前的 Model 一样,负责获取用户数据。
  • view_model.py: UserViewModel 类负责将 User 对象的数据转换成 View 可以直接使用的 user_nameuser_email 属性。它还提供了一个 load_user 方法,用于从 Model 获取用户数据并更新自身。
  • view.py: UserView 类负责展示 user_nameuser_email 属性。 on_load_button_clicked 方法模拟点击按钮,调用 ViewModel 的 load_user 方法。

MVVM 的优点:

  • 数据绑定: View 和 ViewModel 之间通过数据绑定进行交互,减少了代码量,提高了开发效率。
  • 可测试性高: 可以单独测试 ViewModel,更容易进行单元测试。
  • View 更易于设计: View 只需要关注数据的展示,不需要关心数据的获取和处理。

MVVM 的缺点:

  • 学习成本高: 需要学习数据绑定的概念和技术。
  • 调试困难: 数据绑定可能会隐藏一些错误,导致调试困难。
  • 对于简单的UI,可能过度设计

常见的 Python MVVM 应用场景:

  • WPF 应用 (使用 IronPython): WPF 是一个用于构建桌面应用的 UI 框架,它天然支持 MVVM 模式。
  • Web 应用 (使用 JavaScript 框架): 例如使用 Vue.js、React 或 Angular 开发的前端应用。 虽然Python后端不直接用MVVM, 但是前端使用这些框架,整体架构上可以认为是MVVM模式

总结:MVC、MVP 和 MVVM 的对比

为了更清晰地了解这三种架构模式,咱们来个表格对比一下:

特性 MVC MVP MVVM
核心 Controller Presenter ViewModel
View 与 Model 的交互 View 可以直接访问 Model View 不直接访问 Model,通过 Presenter 交互 View 通过数据绑定与 ViewModel 交互
复杂性 相对简单 中等 较高
可测试性 中等 较高 很高
适用场景 中小型 Web 应用,简单的 GUI 应用 复杂的 GUI 应用,复杂的 Web 应用 数据驱动型 UI 应用,使用数据绑定的 Web 应用
耦合度 View 与 Model 有一定的耦合度 View 与 Model 解耦,但 Presenter 依赖 View View 与 Model 完全解耦,通过数据绑定交互
学习曲线 较低 中等 较高

选哪个?看情况!

选择哪种架构模式,取决于你的项目规模、复杂度和团队经验。

  • 小型项目: MVC 足矣。
  • 中型项目: MVP 或许更合适。
  • 大型项目: MVVM 可能是更好的选择。

一些补充说明:

  • 这三种架构模式并不是互斥的。 你可以在同一个项目中同时使用多种架构模式。
  • 架构模式只是一个指导原则, 不必严格遵守。重要的是保持代码的清晰和可维护性。
  • 过度设计比没有设计更糟糕。 不要为了使用某种架构模式而强行使用,选择最适合你的项目的方案。

结尾:架构之路,永无止境

架构模式只是软件开发中的一个方面,还有很多其他的技术需要学习和掌握。希望今天的分享能帮助你更好地理解 MVC、MVP 和 MVVM,并在实际项目中灵活运用。记住,架构之路,永无止境!

好了,今天的分享就到这里。谢谢大家!希望下次有机会再和大家一起聊聊代码那些事儿。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注