名词(术语)了解–架构-MVC/MVVM/MVP
MVC
-
模型(Model)
- 负责应用程序的数据逻辑和业务规则
- 维护数据的状态和完整性
- 与数据库等持久化层交互
- 不依赖于视图和控制器
- 当数据发生变化时,通知相关的视图进行更新
-
视图(View)
- 负责数据的可视化展示
- 将模型的数据呈现给用户
- 接收用户的操作输入
- 可以存在多个视图展示同一个模型的数据
- 不直接处理业务逻辑
-
控制器(Controller)
- 作为模型和视图之间的中介
- 处理用户的交互请求
- 更新模型的状态
- 选择合适的视图进行展示
- 协调整个应用程序的工作流程
MVC的工作流程:
- 用户通过视图界面进行操作
- 控制器接收用户的请求
- 控制器调用相应的模型进行数据处理
- 模型返回处理结果给控制器
- 控制器选择合适的视图进行展示
- 视图更新界面显示
MVC的优势:
- 关注点分离:每个组件负责特定的功能,使代码结构清晰
- 代码复用:模型可以被多个视图复用
- 并行开发:不同团队可以同时开发不同组件
- 维护性好:修改某一组件不会影响其他组件
实际应用示例:
# Model
class UserModel:
def __init__(self):
self.name = ""
self.email = ""
def save(self):
# 保存用户数据到数据库
pass
# View
class UserView:
def show_user_details(self, name, email):
print(f"User: {name}")
print(f"Email: {email}")
def get_user_input(self):
name = input("Enter name: ")
email = input("Enter email: ")
return name, email
# Controller
class UserController:
def __init__(self):
self.model = UserModel()
self.view = UserView()
def create_user(self):
name, email = self.view.get_user_input()
self.model.name = name
self.model.email = email
self.model.save()
self.view.show_user_details(name, email)
MVC模式在现代框架中的应用:
-
Web开发:
- Django (Python):MTV模式(Model-Template-View,本质是MVC)
- Spring MVC (Java)
- Ruby on Rails:严格遵循MVC模式
-
移动开发:
- iOS:Cocoa MVC
- Android:Activity/Fragment可以看作Controller和View的组合
-
桌面应用:
- JavaFX
- Windows Forms
MVVM
class UserModel {
id: number;
name: string;
email: string;
constructor(data: any) {
this.id = data.id;
this.name = data.name;
this.email = data.email;
}
async save() {
// 保存数据到服务器
}
}
<template>
<div>
<input v-model="viewModel.userName" />
<button @click="viewModel.saveUser">保存</button>
<p>{{ viewModel.userStatus }}</p>
</div>
</template>
class UserViewModel {
private model: UserModel;
public userName: string = '';
public userStatus: string = '';
constructor(model: UserModel) {
this.model = model;
this.userName = model.name;
}
async saveUser() {
try {
this.userStatus = '保存中...';
await this.model.save();
this.userStatus = '保存成功';
} catch (error) {
this.userStatus = '保存失败';
}
}
}
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('')
const lastName = ref('')
// 计算属性示例
const fullName = computed(() => {
return firstName.value + ' ' + lastName.value
})
</script>
<template>
<input v-model="firstName" />
<input v-model="lastName" />
<p>全名: {{ fullName }}</p>
</template>
a. 数据绑定
- 自动同步数据和UI
- 减少手动DOM操作
- 提高开发效率
b. 命令模式
// ViewModel中的命令
class ViewModel {
saveCommand = {
execute: async () => {
// 执行保存操作
},
canExecute: () => {
// 判断是否可以执行
return this.isValid;
}
}
}
c. 状态管理
// Vue.js中的状态管理
const store = {
state: reactive({
count: 0
}),
increment() {
this.state.count++
}
}
a. Vue.js中的MVVM实现:
<!-- 组件示例 -->
<template>
<div>
<input v-model="user.name" />
<p>{{ user.displayName }}</p>
<button @click="saveUser">保存</button>
</div>
</template>
<script>
export default {
data() {
return {
user: {
name: '',
displayName: computed(() => `用户: ${this.user.name}`)
}
}
},
methods: {
saveUser() {
// 保存用户信息
}
}
}
</script>
b. Angular中的MVVM实现:
// 组件类
@Component({
selector: 'app-user',
template: `
<input [(ngModel)]="user.name" />
<p>{{user.displayName}}</p>
<button (click)="saveUser()">保存</button>
`
})
class UserComponent {
user = {
name: '',
get displayName() {
return `用户: ${this.name}`;
}
};
saveUser() {
// 保存用户信息
}
}
- 更好的关注点分离
- 更容易进行单元测试
- 更好的代码复用
- 更好的可维护性
- 降低视图和业务逻辑的耦合
- 保持ViewModel的纯粹性
- 避免在View中写业务逻辑
- 使用计算属性处理数据转换
- 合理使用数据绑定
- 注意性能优化
MVP
class UserModel {
private id: number;
private name: string;
private email: string;
constructor(data: any) {
this.id = data.id;
this.name = data.name;
this.email = data.email;
}
async save(): Promise<boolean> {
// 保存数据到服务器
return true;
}
validate(): boolean {
return this.email.includes('@');
}
}
// 视图接口
interface IUserView {
setName(name: string): void;
setEmail(email: string): void;
showError(message: string): void;
showSuccess(message: string): void;
}
// 具体视图实现
class UserView implements IUserView {
private nameElement: HTMLInputElement;
private emailElement: HTMLInputElement;
private presenter: UserPresenter;
constructor() {
this.presenter = new UserPresenter(this);
this.bindEvents();
}
private bindEvents() {
document.getElementById('saveButton')
.addEventListener('click', () => {
this.presenter.onSaveClicked();
});
}
setName(name: string): void {
this.nameElement.value = name;
}
setEmail(email: string): void {
this.emailElement.value = email;
}
showError(message: string): void {
alert(message);
}
showSuccess(message: string): void {
alert(message);
}
}
class UserPresenter {
private view: IUserView;
private model: UserModel;
constructor(view: IUserView) {
this.view = view;
this.model = new UserModel({});
}
async onSaveClicked() {
if (!this.model.validate()) {
this.view.showError('数据验证失败');
return;
}
try {
const success = await this.model.save();
if (success) {
this.view.showSuccess('保存成功');
} else {
this.view.showError('保存失败');
}
} catch (error) {
this.view.showError('发生错误');
}
}
}
a. 严格的分层
// 视图层只负责UI展示
interface IView {
render(data: any): void;
showLoading(): void;
hideLoading(): void;
}
// Presenter层处理业务逻辑
class Presenter {
private view: IView;
private model: Model;
handleUserAction() {
this.view.showLoading();
this.model.process()
.then(data => {
this.view.render(data);
this.view.hideLoading();
});
}
}
b. 视图接口化
// 定义视图接口
interface ILoginView {
getUserName(): string;
getPassword(): string;
showError(message: string): void;
navigateToHome(): void;
}
// 实现视图接口
class LoginActivity implements ILoginView {
private presenter: LoginPresenter;
constructor() {
this.presenter = new LoginPresenter(this);
}
getUserName(): string {
return document.getElementById('username').value;
}
getPassword(): string {
return document.getElementById('password').value;
}
showError(message: string): void {
// 显示错误信息
}
navigateToHome(): void {
// 导航到主页
}
}
a. Android中的MVP实现:
// View接口
interface MainView {
fun showProgress()
fun hideProgress()
fun setItems(items: List<String>)
}
// Presenter
class MainPresenter(private val view: MainView) {
private val model = MainModel()
fun loadItems() {
view.showProgress()
model.getItems { items ->
view.hideProgress()
view.setItems(items)
}
}
}
// Activity实现View接口
class MainActivity : AppCompatActivity(), MainView {
private lateinit var presenter: MainPresenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
presenter = MainPresenter(this)
presenter.loadItems()
}
override fun showProgress() {
progressBar.visibility = View.VISIBLE
}
override fun hideProgress() {
progressBar.visibility = View.GONE
}
override fun setItems(items: List<String>) {
// 更新UI
}
}
b. Web应用中的MVP实现:
// View接口
interface IProductView {
displayProducts(products: Product[]): void;
showLoadingIndicator(): void;
hideLoadingIndicator(): void;
}
// Presenter
class ProductPresenter {
constructor(
private view: IProductView,
private model: ProductModel
) {}
async loadProducts() {
this.view.showLoadingIndicator();
try {
const products = await this.model.getProducts();
this.view.displayProducts(products);
} finally {
this.view.hideLoadingIndicator();
}
}
}
// View实现
class ProductPage implements IProductView {
private presenter: ProductPresenter;
constructor() {
this.presenter = new ProductPresenter(this, new ProductModel());
}
displayProducts(products: Product[]): void {
// 渲染产品列表
}
showLoadingIndicator(): void {
// 显示加载指示器
}
hideLoadingIndicator(): void {
// 隐藏加载指示器
}
}
- 关注点分离更彻底
- 视图可以轻易替换
- 便于单元测试
- 业务逻辑可复用
- 维护性更好
- 保持View的简单性
- Presenter不应该包含Android/iOS等平台特定代码
- 使用接口定义View和Presenter的交互契约
- 避免在Presenter中直接操作UI元素
- 合理处理View的生命周期