簡介
前面我們介紹過了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編程,這樣其實也不需要寫那麼多代碼的,有感興趣或者有想法的小夥伴可以聯繫我。