以分页场景谈MVC设计模式

news2025/1/10 2:55:21

一 、需求场景

在这里插入图片描述
需要实现一个分页组件, 可以方便的进行分页操作。

二、分析需求

从分页需求出发,分析潜在的元素, 虽然只包含一个大的分页功能,但是潜在的元素
包含:上一页 下一页 首页 尾页 当前页 等等。

为什么包含这些元素 ?==> 理解业务的能力

梳理元素之间的关系

上一页 == 当前页左移
下一页 == 当前页右移
首页 == 当前页左移到第一个
尾页 == 当前页右移动到最后一个
切换页码== 重新建立一个上一页、下一页、首页、尾页、当前页之间的关系逻辑

三、代码设计

1. 使用MVC模式梳理

为什么选择MVC模式
为什么不上来就干 (基本的从上至下,从左至右的标准规则)
可不可以选择其他设计模式

1.1 MVC简介

MVC是三个单词的首字母缩写,它们是Model(模型)、View(视图)和Controller(控制)。
在这里插入图片描述

1.2 分析和创建模型

抽象模型是最难的事在于:

  1. 命名
    这个类要不要作为基类,使用场景多不多,起什么样的名字更贴合意义呢?
  2. 属性建立
    在这个业务场景中的属性够不够,增加属性会不会打破属性间的逻辑关系。

2 建立分页model

基于需求中的元素,建立模型
在这里插入图片描述

  • setCurrentPage 公共的可以改变模型中属性的方法
    第一个参数改变当前页的大小,第二个参数改变每页的大小 ,随着当前页和每页大小的改变,随之触发上一页 、下一页、首页、尾页的变化
import lombok.Getter;
import lombok.ToString;

import java.io.Serializable;

/**
 * 分页对象类
 */
@Getter
@ToString
public class Pager implements Serializable {

    private static final long serialVersionUID = 4542617637761955078L;
    /**
     * 当前页
     */
    private int currentPage;
    /**
     * 每页大小
     */
    private int pageSize;
    /**
     * 总页数
     */
    private int pageTotal;
    /**
     * 总条数
     */
    private int totalCount;
    /**
     * 前一页
     */
    private int prevPage;
    /**
     * 下一页
     */
    private int nextPage;
    /**
     * 第一页
     */
    private int firstPage;
    /**
     * 最后一页
     */
    private int lastPage;

    public Pager(int totalCount) {
        this(totalCount, 1, 10);
    }

    public Pager(int totalCount, int currentPage, int pageSize) {
        this.totalCount = totalCount;
        this.firstPage = 1;
        this.setCurrentPage(currentPage, pageSize);
    }

    public void setCurrentPage(int currentPage) {
        this.setCurrentPage(currentPage, this.pageSize);
    }

    /**
     * 设置当前页
     *
     * @param currentPage 当前页
     * @param pageSize    每页数量
     */
    public void setCurrentPage(int currentPage, int pageSize) {
        this.currentPage = currentPage;
        if (this.pageSize != pageSize) {
            this.pageSize = pageSize;
            this.pageTotal = this.totalCount % this.pageSize > 0 ? this.totalCount / this.pageSize + 1 : this.totalCount / this.pageSize;
            this.lastPage = this.pageTotal;
        }

        if (this.currentPage > this.pageTotal) {
            this.currentPage = this.pageTotal;
        }
        if (currentPage > 1) {
            this.prevPage = this.currentPage - 1;
        } else {
            this.prevPage = this.firstPage;
        }
        if (this.currentPage < this.lastPage) {
            this.nextPage = this.currentPage + 1;
        } else {
            this.nextPage = this.lastPage;
        }
    }


}

3. 建立view和control

视图层:
包含了元素的布局、样式 和基本的动作事件及约束
控制层:
包含了 view和视图之间的逻辑关系, 所以最核心的方法currentPageChange
随着当前页和分页大小的变化,控制model和view的变化
在这里插入图片描述

3.1 控制view

所有的view的元素事件都与该方法绑定,根据model的变化,更改view中上一页、下一页 、首页、尾页最大页等元素的状态变化

3.2 控制model

将currentPage当前页和pageSize分页大小的变化传递给model,自动控制上一页、下一页、尾页和首页的属性变化


    private void currentPageChange(int currentPage, int pageSize) {
        pager.setCurrentPage(currentPage, pageSize);
        limitNumberDocument.setMax(pager.getLastPage());
        totalPage.setText("/" + pager.getPageTotal());

        jump.setText(pager.getCurrentPage() + "");
        if (currentPage <= pager.getFirstPage()) {
            first.setEnabled(false);
            prev.setEnabled(false);
            next.setEnabled(true);
            last.setEnabled(true);
        } else if (currentPage >= pager.getLastPage()) {
            first.setEnabled(true);
            prev.setEnabled(true);
            next.setEnabled(false);
            last.setEnabled(false);
        } else {
            first.setEnabled(true);
            prev.setEnabled(true);
            next.setEnabled(true);
            last.setEnabled(true);
        }


    }

3.3 完整示例

import cn.hutool.core.util.StrUtil;
import cn.note.swing.core.document.LimitNumberDocument;
import cn.note.swing.core.event.ConsumerAction;
import cn.note.swing.core.event.key.KeyActionFactory;
import cn.note.swing.core.util.FrameUtil;
import cn.note.swing.core.view.AbstractMigView;
import cn.note.swing.core.view.base.SelectedComboBox;
import cn.note.swing.core.view.form.SelectedItem;
import cn.note.swing.core.view.theme.ThemeFlatLaf;
import net.miginfocom.swing.MigLayout;

import javax.swing.*;

/**
 * 分页测试
 */
public class PaginationTest extends AbstractMigView {

    /* 分页模型*/
    private Pager pager;

    /* 第一页*/
    private JButton first;

    /*上一页*/
    private JButton prev;

    /* 下一页*/
    private JButton next;

    /* 最大页数 */
    private JButton last;

    /* 总页数 */
    private JLabel totalPage;

    /* 回车跳转*/
    private JTextField jump;

    /* 下拉选择*/
    private SelectedComboBox selectItems;

    /* 最大数限制*/
    private LimitNumberDocument limitNumberDocument;

    @Override
    protected MigLayout defineMigLayout() {
        return new MigLayout("");
    }

    @Override
    protected void render() {
        pager = new Pager(100);
        first = new JButton("《");
        prev = new JButton("<");
        next = new JButton(">");
        last = new JButton("》");
        totalPage = new JLabel("/" + pager.getPageTotal());
        jump = new JTextField();
        // 限制最大输入页数
        limitNumberDocument = new LimitNumberDocument(pager.getLastPage());
        jump.setDocument(limitNumberDocument);
        
        // 条数
        selectItems = new SelectedComboBox();
        selectItems.addSelectItem(new SelectedItem("10", "10 条/页"));
        selectItems.addSelectItem(new SelectedItem("20", "20 条/页"));
        selectItems.addSelectItem(new SelectedItem("30", "30 条/页"));
        selectItems.addSelectItem(new SelectedItem("40", "40 条/页"));
        selectItems.addSelectItem(new SelectedItem("50", "50 条/页"));


        view.add(first);
        view.add(prev);
        view.add(jump);
        view.add(totalPage);
        view.add(next);
        view.add(last);
        view.add(selectItems);

        handleActions();
        currentPageChange(1, 10);
    }


    /**
     * 拦截所有动作
     */
    private void handleActions() {
        // 第一页 上一页 下一页 最后一页
        first.addActionListener(e -> currentPageChange(pager.getFirstPage()));
        prev.addActionListener(e -> currentPageChange(pager.getPrevPage()));
        next.addActionListener(e -> currentPageChange(pager.getNextPage()));
        last.addActionListener(e -> currentPageChange(pager.getLastPage()));


        // 回车限制输入不合法时,重置为1
        KeyActionFactory.bindEnterAction(jump, new ConsumerAction(e -> {
            String text = jump.getText();
            if (StrUtil.isBlank(text) || Integer.valueOf(text) == 0) {
                jump.setText(String.valueOf(pager.getFirstPage()));
            }
            currentPageChange(Integer.valueOf(jump.getText()));
        }));


        // 变换条数
        selectItems.onSelected(item -> {
            currentPageChange(pager.getCurrentPage(), Integer.valueOf(item.getKey()));
        });
    }


    /**
     * 当前页变化
     *
     * @param currentPage 当前页
     */
    private void currentPageChange(int currentPage) {
        currentPageChange(currentPage, pager.getPageSize());
    }

    /**
     * 当前页变化事件
     *
     * @param currentPage 当前页
     * @param pageSize    分页大小
     */
    private void currentPageChange(int currentPage, int pageSize) {
        pager.setCurrentPage(currentPage, pageSize);
        limitNumberDocument.setMax(pager.getLastPage());
        totalPage.setText("/" + pager.getPageTotal());

        jump.setText(pager.getCurrentPage() + "");
        if (currentPage <= pager.getFirstPage()) {
            first.setEnabled(false);
            prev.setEnabled(false);
            next.setEnabled(true);
            last.setEnabled(true);
        } else if (currentPage >= pager.getLastPage()) {
            first.setEnabled(true);
            prev.setEnabled(true);
            next.setEnabled(false);
            last.setEnabled(false);
        } else {
            first.setEnabled(true);
            prev.setEnabled(true);
            next.setEnabled(true);
            last.setEnabled(true);
        }


    }

    public static void main(String[] args) {
        ThemeFlatLaf.install();
        FrameUtil.launchTime(PaginationTest.class);
    }
}

4. 包装为组件及 美化

4.1 使用图标及样式代码进行美化

在这里插入图片描述

4.2 使用抽象进行组件封装加入与外部交互

  1. 添加Consumer 进行pager变换的传递
    在这里插入图片描述
    2. 在currentChange中 触发该方法
    在这里插入图片描述

5. 实战与表格分页

当你的表格需要分页时, 那么你就可以把你包装的分页组件加进来

  • 代码是轻松维护的, 表格逻辑和分页逻辑无关
  • 代码是可扩展的, 改变分页的风格不会影响表格代码, 因为表格只绑定了分页的model

MVC的设计模式, 没有增加代码量 , 但是让代码具备低耦合和高扩展的特性

在这里插入图片描述
请添加图片描述

四、 语言的互通

1. MVC 在JavaScript中适配

将上述场景,使用MVC还原在h5中
在这里插入图片描述

使用类构建js代码容易维护吗?

2. 完整示例

<html>
<body>
  <div>
    <button id="first"></button>
    <button id="prev"></button>
    <!-- 限制只能输入数字 -->
    <input type="text" id="jump" onkeyup="this.value=this.value.replace(/[^\d]/g,'') "
      onafterpaste="this.value=this.value.replace(/[^\d]/g,'') ">
    <label id="totalPage"></label>
    <button id="next"></button>
    <button id="last"></button>
    <select id="pageSizeSelect">
      <option value="10">10 条/页</option>
      <option value="20">20 条/页</option>
      <option value="30">30 条/页</option>
      <option value="40">40 条/页</option>
      <option value="50">50 条/页</option>
    </select>
  </div>
</body>
<script>
  class Pager {
    /*当前页*/
    currentPage;
    /*每页大小*/
    pageSize;
    /*总页数*/
    totalPage;
    /*总条数*/
    totalCount;
    /*前一页*/
    prevPage;
    /*下一页*/
    nextPage;
    /*第一页*/
    firstPage;
    /*最后一页*/
    lastPage;

    constructor(totalCount, currentPage = 1, pageSize = 10) {
      this.totalCount = totalCount;
      this.firstPage = 1;
      this.setCurrentPage(currentPage, pageSize);
    }

    /**
     * 设置当前页
     *
     * @param currentPage 当前页
     * @param pageSize    每页数量
     */
    setCurrentPage(currentPage, pageSize) {
      this.currentPage = Number(currentPage);
      if (this.pageSize != pageSize) {
        this.pageSize = pageSize;
        this.totalPage = this.totalCount % this.pageSize > 0 ? Math.floor(this.totalCount / this.pageSize) + 1 : Math.floor(this.totalCount / this.pageSize);
        this.lastPage = this.totalPage;
      }

      if (this.currentPage > this.totalPage) {
        this.currentPage = this.totalPage;
      }
      if (this.currentPage > 1) {
        this.prevPage = this.currentPage - 1;
      } else {
        this.prevPage = this.firstPage;
      }
      if (this.currentPage < this.lastPage) {
        this.nextPage = this.currentPage + 1;
      } else {
        this.nextPage = this.lastPage;
      }
    }
  }


  class PaginationTest {

    /* 分页模型*/
    pager;
    /* 第一页*/
    first;
    /*上一页*/
    prev;
    /* 下一页*/
    next;
    /* 最大页数 */
    last;
    /* 总页数 */
    totalPage;
    /* 回车跳转*/
    jump;
    /* 下拉选择*/
    pageSizeSelect;

    constructor(totalCount) {
      this.pager = new Pager(totalCount);
      this.first = document.getElementById("first");
      this.prev = document.getElementById("prev");
      this.next = document.getElementById("next");
      this.last = document.getElementById("last");
      this.totalPage = document.getElementById("totalPage");
      this.jump = document.getElementById("jump");
      this.pageSizeSelect = document.getElementById("pageSizeSelect");

      this.handleActions();
      this.currentPageChange(1, 10);
    }
    /**
     * 拦截所有动作
     */
    handleActions() {
      this.first.onclick = () => { this.currentPageChange(this.pager.firstPage, this.pager.pageSize) }
      this.prev.onclick = () => { this.currentPageChange(this.pager.prevPage, this.pager.pageSize) }
      this.next.onclick = () => { this.currentPageChange(this.pager.nextPage, this.pager.pageSize) }
      this.last.onclick = () => { this.currentPageChange(this.pager.lastPage, this.pager.pageSize) }
      this.pageSizeSelect.onchange = (e) => {
        this.currentPageChange(this.pager.currentPage, e.target.value)
        if (this.jump.value > this.pager.totalPage) {
          this.jump.value = this.pager.totalPage
        }
      }

      // 限制最大输入
      this.jump.oninput = () => {
        let v = this.jump.value;
        if (v > this.pager.totalPage) {
          this.jump.value = this.pager.totalPage
        }
      }

      this.jump.onchange = () => {
        let v = this.jump.value;
        if (!v) {
          this.jump.value = 1;
        }
        this.currentPageChange(this.jump.value, this.pager.pageSize)
      }
    }

    /**
     * 当前页变化事件
     *
     * @param currentPage 当前页
     * @param pageSize    分页大小
     */
    currentPageChange(currentPage, pageSize = 10) {
      this.pager.setCurrentPage(currentPage, pageSize);
      this.jump.value = currentPage;
      this.totalPage.innerHTML = "/" + this.pager.totalPage;

      if (currentPage <= this.pager.firstPage) {
        this.first.disabled = true;
        this.prev.disabled = true;
        this.next.disabled = false;
        this.last.disabled = false;
      } else if (currentPage >= this.pager.lastPage) {
        this.first.disabled = false;
        this.prev.disabled = false;
        this.next.disabled = true;
        this.last.disabled = true;
      } else {
        this.first.disabled = false;
        this.prev.disabled = false;
        this.next.disabled = false;
        this.last.disabled = false;
      }
    }
  }
  new PaginationTest(100);
</script>
</html>

五、后话

  • 要不要建立模型 ,你给抽通用模型的时间了,催催催?
  • 抽象完的代码能坚持多久,这项目不知道哪天就黄了,还有复用的可能 ?
  • 封装为组件时,控制层需要释放哪些属性 ,那些user天天要我改组件怎么办?
  • 业务代码和非业务代码 使用不使用MVC模式的重点在于,需求变化的可控性 (至上)和程序员的一点骄傲(唯心)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/118509.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Oauth2】SpringBoot整合Oauth2实现认证授权

SpringBoot整合Oauth2实现认证授权 应用场景 OAuth2.0 协议的使用场景主要为&#xff1a;第三方登录和开放接口的调用 第三方登录就是使用微信等第三方的方式来登录一个应用或者网站&#xff0c;比如用微信账号登录gitee。 而开发接口的调用&#xff0c;则比如说微信、美团…

TP5的消息队列

1.首先查看项目中是否已经有think-queue目录&#xff1a;/vendor/topthink/ 微信截图_20200909142126.png 如果没有&#xff0c;则用composer安装&#xff08;安装composer参考&#xff1a;http://www.runoob.com/w3cnote/composer-install-and-usage.html &#xff09;&#x…

Qt文档阅读笔记-Qt, QML, Widgets…What Is The Difference?

Qt, QML, Widgets…What Is The Difference? 本节主要介绍了开发Qt程序最关机的几个组建。 Qt是使用C和一些C的框架设计编写出来的。 Qt Qt是一个开源的框架。 Qt作为一个框架&#xff0c;包含了许多组件&#xff0c;这些组建又在指定的模块中&#xff0c;Qt基础组件在&…

干货| 小游戏赛道变现指南

随着羊了个羊等小游戏的爆火&#xff0c;不少人发现了小游戏赛道的巨大潜力&#xff0c;也想要在此赛道有所尝试。但是很多游戏赛道的新人对于小游戏变现问题存有疑问&#xff0c;今天就来跟大家分享一下小程序游戏赛道的变现途径&#xff01; 近期FinClip 官方正在举行小游戏…

全志 芯片 Linux MIPI CSI摄像头接口开发指南 VIN DVP CSI MIPI V4l2

1 前言 1.1 文档简介 介绍 VIN&#xff08;video input&#xff09;驱动配置&#xff0c;API 接口和上层使用方法。 1.2 目标读者 camera 驱动开发、维护人员和应用开发人员。 1.3 适用范围 ​ 表 1-1: 适用产品列表 内核版本驱动文件Linux-4.9drivers/media/platform/s…

nacos源码分析-服务注册(服务端)

安装Nacos源码 上一篇文章我们了解了《Nacos服务注册》客户端源码&#xff0c;本篇文章我们来看一下服务注册Nacos服务端的源码执行情况。首先需要下载Nacos源码&#xff0c; https://github.com/alibaba/nacos/releases/tag/1.4.3 &#xff0c; 解压之后使用IDEA工具导入即可…

Web3中文|为什么去中心化存储对NFT元数据很重要

图中文字&#xff1a;哦&#xff0c;看&#xff0c;FTX用Web2 API托管了所有在其平台上铸造的NFT&#xff0c;现在所有这些NFT的元数据都被破坏了&#xff0c;并且链接到了一个重组的网站。 这本不应该发生。但对于任何不考虑元数据和如何存储元数据的NFT项目来说&#xff0c;…

docker(5):Dockerfile

目录Dockerfile介绍Dockerfile常用指令案例&#xff1a;构建tomcat镜像Dockerfile介绍 Dockerfile 是一个用来构建镜像的文本文件&#xff0c;文本内容包含了一条条构建镜像所需的指令和说明&#xff0c;每条指令都会创建一个新的镜像层并对镜像进行提交。 Dockerfile 一般分…

【Django】第一课 基于Django图书借阅管理网站平台

概念 django服务器开发框架是一款基于Python编程语言用于web服务器开发的框架&#xff0c;采用的是MTV架构模式进行分层架构。 项目搭建 打开pycharm开发软件&#xff0c;打开开发软件的内置dos窗口操作命令行 在这里指定项目存放的磁盘路径&#xff0c;并使用创建django项目…

编辑器:保存格式化修复配置

规范化条目 制表符长度&#xff1a;2&#xff0c;缩进模式&#xff1a;2个空格&#xff0c;换行符&#xff1a;lf&#xff0c;末尾加分号&#xff0c;js单引号&#xff0c;冒号后一个空格&#xff0c;运算符前后一个空格&#xff0c;大括号&#xff08;有内容的&#xff09;首…

项目实战之旅游网(八)后台产品管理(下)

目录 一.上传产品图片 二.修改产品 三.上下架产品 一.上传产品图片 在新增产品时&#xff0c;我们还需要上传产品图片。我们采用异步上传的方法进行图片上传。 1.在conmmon_ resources.html 中引入jqueryform.js 2.修改product_ add.html 页面 点击保存 &#xff0c;自动…

【Spring【IOC】】——18、自定义组件中如何注入Spring底层的组件?

&#x1f4eb;作者简介&#xff1a;zhz小白 公众号&#xff1a;小白的Java进阶之路 专业技能&#xff1a; 1、Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理 2、熟悉Java基础&#xff0c;并精通多线程的开发&#xff0c;熟悉JVM原理&#xff0c;具备⼀定的线…

LeetCode283.移动0

思路1 分析 在i位置遇到0&#xff0c;把后面的元素向前移动覆盖&#xff0c;然后把最后一个位置赋值为0即可 注意问题&#xff1a; 可能 i 一个位置 移动一次之后还是0&#xff0c;需要循环 有可能 i 位置的0 是因为 已经所有的0都到后面了 ​ 所以需要用count记录0的个数&am…

2022年区块链安全领域8成以上损失集中在DeFi和跨链桥

近期&#xff0c;欧科云链研究院上线《2022年全球区块链生态安全态势报告》&#xff0c;报告指出2022年区块链安全领域8成以上损失集中在DeFi和跨链桥&#xff0c;钓鱼攻击是最常见攻击手法。主要结论 2022年前11个月&#xff0c;OKLink共监测到区块链生态相关安全事件275起&a…

整理leetcode中”最长...“

1.最长公共子序列&#xff08;动态规划&#xff09;剑指offer95 输入&#xff1a;text1 “abcde”, text2 “ace” 输出&#xff1a;3 解释&#xff1a;最长公共子序列是 “ace” &#xff0c;它的长度为 3 。 Q1&#xff1a;为什么想到二维dp&#xff1f; A1&#xff1a;因…

JDBC第二章 (JDBC API详解)

目录 一、下载驱动包 二、加载与注册驱动 1、使用driverManager类 2、方式&#xff1a; 3、补充&#xff1a; 三、建立连接 1、URL 2.建立连接的方式 3.事务管理 4.获取Statement语句 1、普通版本 2、防止SQL注入版本 3、获取存储过程 四、Statement 1、概述 2…

数图互通高校房产管理——房屋模拟分配建设

数图互通房产管理系统在这方面做得比较全面&#xff1b; 1、 房屋模拟分配建设方案 实现对学校房屋分配进行情景模拟&#xff0c;在特定房屋类型、数量、使用面积等情况下&#xff0c;建立多个模拟分配方案&#xff0c;并对每个模拟分配方案生成明细清单。 1.1 房屋模拟分配清…

石墨烯太阳能供暖远程监控

石墨烯太阳能供暖系统是指采用全新一代石墨烯碳纤维电热膜为发热体&#xff0c;直接将电能转换为热能的供暖系统。再搭配太阳能光伏发电系统给石墨烯供暖系统供电&#xff0c;更加节能有效地解决用户用电问题。但目前这种供暖方式也存在诸多问题&#xff0c;如供暖温度得不到控…

深度学习交通标志识别项目

主要内容 在本文中&#xff0c;使用Python编程语言和库Keras和OpenCV建立CNN模型&#xff0c;成功地对交通标志分类器进行分类&#xff0c;准确率达96%。开发了一款交通标志识别应用程序&#xff0c;该应用程序具有图片识别和网络摄像头实时识别两种工作方式。 写作目的 近年…

jenkins 升级遇到问题总结

当我在使用jenkins的时候,避免不了下载很多插件,因为jenkins本身不提供很多功能,大部分的功能都是依赖插件来完成的,这也使jenkins更具有扩展性,但是,我在安装完成后打开插件列表居然是这样的。。。 或者插件列表打开的正常,但是安装某个插件时报这样的错误。。。 看标…