前端框架系列之(mvp)

簡介

前面我們介紹過了mvc 前端框架系列之(mvc),最後其實view跟controller的耦合度還是沒有完全分離,所以會導致一大堆邏輯還是在view視圖層了,所以爲了解決這個問題,就把controller換成了presenter。

  • Model(模型)表示應用程序核心(比如數據庫記錄列表)。
  • View(視圖)顯示數據(數據庫記錄)。
  • Presenter(代理)負責邏輯的處理

我們再看一下mvc的設計圖:

在這裏插入圖片描述

再看一下mvp的設計圖:

在這裏插入圖片描述

MVP跟MVC很相像,我們把MVP當成MVC來看也不爲過,presenter就像一個經紀人一樣,view的什麼事情只需要跟經紀人說就可以了(任何事!!)

業務需求

  1. 接收用戶輸入的“用戶名”和“密碼”做登錄操作
  2. 登錄成功後返回“登錄成功提示”

項目搭建

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章