美文网首页
前端框架系列之(mvp)

前端框架系列之(mvp)

作者: vv_小虫虫 | 来源:发表于2020-06-22 21:35 被阅读0次

    简介

    前面我们介绍过了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编程,这样其实也不需要写那么多代码的,有感兴趣或者有想法的小伙伴可以联系我。

    相关文章

      网友评论

          本文标题:前端框架系列之(mvp)

          本文链接:https://www.haomeiwen.com/subject/sroffktx.html