前端框架系列之(mvp)
简介
前面我们介绍过了mvc 前端框架系列之(mvc),最后其实view跟controller的耦合度还是没有完全分离,所以会导致一大堆逻辑还是在view视图层了,所以为了解决这个问题,就把controller换成了presenter。
- Model(模型)表示应用程序核心(比如数据库记录列表)。
- View(视图)显示数据(数据库记录)。
- Presenter(代理)负责逻辑的处理
我们再看一下mvc的设计图:
在这里插入图片描述再看一下mvp的设计图:
在这里插入图片描述MVP跟MVC很相像,我们把MVP当成MVC来看也不为过,presenter就像一个经纪人一样,view的什么事情只需要跟经纪人说就可以了(任何事!!)
业务需求
- 接收用户输入的“用户名”和“密码”做登录操作
- 登录成功后返回“登录成功提示”
项目搭建
通过上一篇的mvc的解析,再到mvp就太简单了,我们直接用上一节的demo
https://github.com/913453448/vue-property-decorator-demo
我们copy一个mvc目录为mvp:
在这里插入图片描述同样,我们修改一下main.ts的入口为mvp/index.vue
main.ts:
import Vue from "vue";
import Demo from "./mvp/index.vue";
new Vue({
render(h){
return h(Demo);
}
}).$mount("#app");
Model
model实现没变,还是跟mvc一样。
UserModelImp.ts
import User from "./User";
/**
* user数据持久化接口层
*/
export default interface IUserModel {
/**
* 用户登录
* @param {string} name
* @param {string} pwd
* @returns {Promise<User>}
*/
login(name: string, pwd: string): Promise<User>
}
UserModelImp.ts:
import User from "./User";
import IUserModel from "./IUserModel";
/**
* user数据持久化实现层
*/
export default class UserModelImp implements IUserModel {
/**
* 用户登录
* @param {string} name
* @param {string} pwd
* @returns {Promise<User>}
*/
login(name: string, pwd: string): Promise<User> {
return new Promise((resolve, reject) => {
if ("123456" === pwd) {
const user = new User();
user.id = "1000";
user.name = name;
user.pwd = pwd;
resolve(user);
} else {
reject(new Error("密码错误"));
}
});
}
};
Presenter
我们把IUserController.ts代码修改一下,让login方法不返回值, 因为前面说了之前mvc的contorller是返回了一个user给view页面让它自己去处理的,现在有了presenter(经纪人)了,全部事情就直接在presenter里面处理就可以了。
IUserPresenter.ts:
/**
* user逻辑处理接口层
*/
export default interface IUserPresenter {
/**
* 用户登录
* @param {string} name
* @param {string} pwd
*/
login(name: string, pwd: string);
}
同样,UserControllerImp.ts修改为UserPresenterImp.ts继承IUserPresenter.
UserPresenterImp.ts:
import IUserPresenter from "./IUserPresenter";
import UserModelImp from "./UserModelImp";
import User from "./User";
/**
* user逻辑处理层
*/
export default class UserPresenterImp implements IUserPresenter {
private userModel = new UserModelImp();
login(name: string, pwd: string): Promise<User> {
return this.userModel.login(name, pwd);
}
}
UserControllerImp.ts我们修改为UserPresenterImp.ts,然后把view层的逻辑全部抽象出去,全部转移到presenter中。
UserPresenterImp.ts:
import IUserPresenter from "./IUserPresenter";
import UserModelImp from "./UserModelImp";
import IUserView from "./IUserView";
/**
* user逻辑处理层
*/
export default class UserPresenterImp implements IUserPresenter {
private userModelImp = new UserModelImp();
private userViewImp: IUserView;
constructor(view: IUserView) {
this.userViewImp = view;
}
login(name: string, pwd: string) {
this.userModelImp.login(name, pwd).then((user) => {
this.userViewImp.showMessage("欢迎你:" + user.name);
}).catch((error) => {
this.userViewImp.showMessage("登录失败-->" + error.message);
});
}
}
View
IUserView.ts中我们把controller换成presenter
IUserView.ts:
/**
* user view接口
*/
import IUserPresenter from "./IUserPresenter";
export default interface IUserView {
/**
* 用户逻辑控制层
*/
userPresenter: IUserPresenter;
/**
* 登录响应
*/
onLogin(): void;
/**
* 展示消息
* @param {string} msg
*/
showMessage(msg: string): void;
}
index.vue中我们把controller换成presenter,然后把业务逻辑删掉
index.vue
<template>
<div>
用户名:<input name="name" v-model="name"><br>
密码:<input name="pwd" v-model="pwd"><br>
<button @click="onLogin">登录</button>
</div>
</template>
<script lang="ts">
import Vue from "vue";
import Component from "../view/component";
import UserPresenterImp from "./UserPresenterImp";
import IUserView from "./IUserView";
@Component
class UserViewImp extends Vue implements IUserView{
userPresenter: UserPresenterImp; //用户逻辑控制层
name = ""; //用户名
pwd = ""; //密码
constructor() {
super();
this.userPresenter = new UserPresenterImp(this as IUserView);
}
/**
* 去登录
*/
onLogin() {
this.userPresenter.login(this.name, this.pwd);
}
/**
* 展示消息
* @param {string} msg
*/
showMessage(msg = "") {
alert(msg);
}
}
export default UserViewImp;
</script>
编译运行
npm run dev
运行结果
在这里插入图片描述在这里插入图片描述
总结
优点
MVP与MVC的主要区别是View与Model不直接交互,而是通过与Presenter来完成交互,这样可以修改视图而不影响模型,达到解耦的目的,实现了Model和View真正的完全分离。视图的变化总是比较频繁,将业务逻辑抽取出来,放在表示器中实现,使模块职责划分明显,层次清晰,一个presenter能复用于多个视图,而不需要更改表示器的逻辑(当然是在该视图的改动不影响业务逻辑的前提下),这增加了程序的复用性。
缺点
MVP的明显缺点是增加了代码的复杂度,特别是针对小型Android应用的开发,会使程序冗余。Presenter中除了应用逻辑以外,还有大量的View->Model,Model->View的手动同步逻辑,会导致Presenter臃肿,维护困难。视图的渲染过程也会放在Presenter中,造成视图与Presenter交互过于频繁,如果某特定视图的渲染很多,就会造成Presenter与该视图联系过于紧密,一旦该视图需要变更,那么Presenter也需要变更了。
个人而言,在多人合作的项目上,如果多写一点代码能让逻辑更清晰的话又何妨呢?至少不会被下一个接手人骂代码“像坨💩了” 😂😂😂,yy一下,我觉得利用打包脚本或者装饰器等工具结合动态代理也可以做到像Spring一样的面向aop编程,这样其实也不需要写那么多代码的,有感兴趣或者有想法的小伙伴可以联系我。