嘿,各位朋友,晚上好!欢迎来到今晚的“架构那些事儿”小讲堂。今天咱们不聊别的,就扒一扒 Python Web 框架里那些个“M”打头的家伙:MVC、MVP 和 MVVM。保证让你听完之后,感觉自己瞬间升了一个段位!
开场白:架构,不止是盖房子
咱们写代码,就像盖房子。一开始搭个小棚子,随便怎么来都行。但房子大了,就得好好设计,不然住着不舒服,还容易塌。架构模式就是盖房子的设计图,它能让你的代码更清晰、更容易维护、扩展也更方便。
第一部分:MVC (Model-View-Controller)
MVC,全称 Model-View-Controller,翻译过来就是“模型-视图-控制器”。这三位是铁三角,关系很密切。
-
Model(模型): 负责处理数据。你可以把它看作是数据库的代理,或者直接就是你的数据对象。它负责数据的获取、存储、更新等操作。
-
View(视图): 负责展示数据。它就是用户看到的界面,比如 HTML 页面。
-
Controller(控制器): 负责接收用户的请求,调用 Model 处理数据,然后选择合适的 View 展示给用户。它就像一个交通警察,指挥着 Model 和 View 之间的交互。
MVC 的工作流程:
- 用户发起请求 (比如点击一个按钮)。
- Controller 接收到请求。
- Controller 调用 Model 去获取或更新数据。
- Model 完成数据操作。
- Controller 选择合适的 View 并将数据传递给 View。
- 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 的工作流程:
- 用户在 View 上发起操作 (比如点击一个按钮)。
- View 将操作传递给 Presenter。
- Presenter 调用 Model 去获取或更新数据。
- Model 完成数据操作。
- Presenter 更新 View。
- 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 的工作流程:
- View 通过数据绑定监听 ViewModel 中的数据变化。
- 用户在 View 上发起操作 (比如点击一个按钮)。
- View 调用 ViewModel 中的命令。
- ViewModel 调用 Model 去获取或更新数据。
- Model 完成数据操作。
- ViewModel 更新自身的数据。
- 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_name
和user_email
属性。它还提供了一个load_user
方法,用于从 Model 获取用户数据并更新自身。view.py
:UserView
类负责展示user_name
和user_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,并在实际项目中灵活运用。记住,架构之路,永无止境!
好了,今天的分享就到这里。谢谢大家!希望下次有机会再和大家一起聊聊代码那些事儿。