前端框架系列之(mvp)

2020-06-22  本文已影响0人  vv_小虫虫

简介

前面我们介绍过了mvc 前端框架系列之(mvc),最后其实view跟controller的耦合度还是没有完全分离,所以会导致一大堆逻辑还是在view视图层了,所以为了解决这个问题,就把controller换成了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编程,这样其实也不需要写那么多代码的,有感兴趣或者有想法的小伙伴可以联系我。

上一篇下一篇

猜你喜欢

热点阅读