图书管理系统

作者: 当战神遇到编程

文章专栏:<<项目专栏>>欢迎大家点赞👍评论📝收藏⭐文章


文章目录

  • [一 项目前置知识](#一 项目前置知识)
    • [1 单例模式](#1 单例模式)
    • [2 工厂模式(本文讲工厂方法模式)](#2 工厂模式(本文讲工厂方法模式))
    • [3 代理模式](#3 代理模式)
      • [3.1 基本概念](#3.1 基本概念)
    • [4 关于jar包的使用](#4 关于jar包的使用)
      • [4.1 什么是jar包?](#4.1 什么是jar包?)
      • [4.2 项目引入jar包](#4.2 项目引入jar包)
  • [二 图书管理系统](#二 图书管理系统)
    • 1.模块划分介绍
    • [2. User模块](#2. User模块)
      • [2.1 User类](#2.1 User类)
      • [2.2 AdminUser(role---管理员写死)](#2.2 AdminUser(role---管理员写死))
      • [2.3 NormalUser(role---普通用户写死)](#2.3 NormalUser(role---普通用户写死))
      • [2.4 引入工厂模式](#2.4 引入工厂模式)
      • [2.5 User模块-引⼊代理模式控制对象权限](#2.5 User模块-引⼊代理模式控制对象权限)
    • 3.book模块
      • [3.1 Book类的定义](#3.1 Book类的定义)
      • [3.2 Library类的定义](#3.2 Library类的定义)
        • [3.2.1 Library中的设计思路](#3.2.1 Library中的设计思路)
        • [3.2.2 AnalyzingBook 类实现⽂件内容的写⼊](#3.2.2 AnalyzingBook 类实现⽂件内容的写⼊)
        • [3.2.3 Book类的toJSON的实现](#3.2.3 Book类的toJSON的实现)
        • [3.2.4 AnalyzingBook 类实现⽂件内容的读取](#3.2.4 AnalyzingBook 类实现⽂件内容的读取)
        • [3.2.5 Library⽂件&读取存储具体细节设计](#3.2.5 Library⽂件&读取存储具体细节设计)
          • [3.2.5.1 ⽂件内容读取到内存中进⾏存储](#3.2.5.1 ⽂件内容读取到内存中进⾏存储)
          • [3.2.5.2 内存中的书籍数组进⾏存储到⽂件](#3.2.5.2 内存中的书籍数组进⾏存储到⽂件)
    • 4业务操作逻辑框架分解
    • [5 管理员端业务实现](#5 管理员端业务实现)
    • [6 普通⽤户端业务实现](#6 普通⽤户端业务实现)

一 项目前置知识

1 单例模式

单例模式是一种设计模式,核心目标是:确保某一个类只有一个实例,并提供一个全局唯一的访问点。

单例模式的实现主要有两种⽅式:饿汉式和懒汉式。

(1)饿汉式(EagerInitialization) :在应⽤程序启动 时就进⾏单例对象的初始化,⽆论是否会被使⽤。优点是不需要考虑多线程环境下的线程安全性,访问单例对象时不会引⼊额外的性能开销。缺点是可能会浪费系统资源,因为单例对象在应⽤程序启动时就被创建,如果⼀直未被使⽤,可能会占⽤内存

java 复制代码
public class Singleton {
    private static Singleton instance = new Singleton();
    //控制实例个数
    private Singleton(){
        
    }
    public static Singleton getInstance(){
        return instance;
    }
    
}

(2)懒汉式(LazyInitialization):延迟加载,即单例对象在⾸次访问时才进⾏初始化。优点是节省了系统资源,只有在需要时才创建单例对象。缺点是在多线程环境下,可能会出现竞态条件,需要额外的线程安全措施来确保只创建⼀个实例。

java 复制代码
public class Singleton2 {
    private static Singleton2 instance;
    private Singleton2(){
        
    } 
    public static Singleton2 getInstance(){
        if(instance == null){
            instance = new Singleton2();
        }
        return instance;
    }
}

单例模式(懒汉式)的应用

java 复制代码
public class Principal {
    private static Principal instance;
    private String name;
    private Principal(){
        this.name = "王校长";
    }
    public static Principal getInstance(){
        if(instance == null){
            instance = new Principal();
        }
        return instance;
    }
    public void announcement(String message){
        System.out.println(name + "通知" + message);
    }
    public void approveDocument(String file){
        System.out.println(name + "审批通过" + file);
    }
}
class School {
    public static void main(String[] args) {
        Principal principal1 = Principal.getInstance();
        principal1.announcement(" 全校明天开始放假 3 天 ");
        Principal principal2 = Principal.getInstance();
        principal2.approveDocument("⻝堂伙⻝改善⽂件 ");
        //检查是否是同⼀个校⻓实例
        System.out.println("上述两个是同⼀位校⻓吗? " + (principal1 == principal2));
    }
}
//运行结果
王校长通知 全校明天开始放假 3 天 
王校长审批通过⻝堂伙⻝改善⽂件 
上述两个是同⼀位校⻓吗? true

2 工厂模式(本文讲工厂方法模式)

⼯⼚模式(FactoryPattern)是最常⽤的设计模式之⼀,它提供了⼀种创建对象的⽅式,使得创建对象的过程与使⽤对象的过程分离。

简单来说,⼯⼚模式通过创建⼀个专⻔负责创建对象的⼯⼚类,将对象的创建过程封装起来,以此达到解耦的⽬的。从⽽达到了解耦。

使⽤⼯⼚⽅法模式:

(1)创建⼯⼚接⼝【抽象⼯⼚】

java 复制代码
package user.factory;

import user.User;

public interface IUserFactory {
    User createUser(String name, int ID);
}

(2)具体⼯⼚

java 复制代码
package user.factory;

import user.AdminUser;
import user.User;

public class AdminUserFactory implements IUserFactory{
    @Override
    public User createUser(String name, int ID) {
        return new AdminUser(name,ID);
    }
}
java 复制代码
package user.factory;

import user.NormalUser;
import user.User;

public class NormalUserFactory implements IUserFactory {
    @Override
    public User createUser(String name, int ID) {
        return new NormalUser(name,ID);
    }
}

(3)抽象产品

java 复制代码
package user;

public abstract class User {
    protected String name;  // ⽤户名
    protected int userID;   // ⽤户ID
    public User(String name, int userID) {
        this.name = name;
        this.userID = userID;
    }
}

(4)具体产品

java 复制代码
package user;

public class NormalUser extends User{
    public NormalUser(String name, int userID) {
        super(name, userID);
    }
}
java 复制代码
package user;

public class AdminUser extends User {
    public AdminUser(String name, int userID) {
        super(name, userID);
    }
}

(5)实现方式

java 复制代码
package user;

import user.factory.AdminUserFactory;
import user.factory.IUserFactory;
import user.factory.NormalUserFactory;

public class UserManagement {
    public static void main(String[] args) {
        IUserFactory adminFactory = new AdminUserFactory();
        User adminUser = adminFactory.createUser(" 刘备 ", 1);
        IUserFactory normalFactory = new NormalUserFactory();
        User normalUser1 = normalFactory.createUser(" 关⽻ ", 2);
        User normalUser2 = normalFactory.createUser(" 张⻜ ",3);
    }
}

• 在⼯⼚⽅法模式中,每种⽤户类型都有⾃⼰的⼯⼚类。要添加新的⽤户类型,只需创建新的⼯⼚类,⽆需修改现有代码。

• 代码不会重复,创建对象的过程,封装到了⼯⼚当中

• 更加解耦

3 代理模式

3.1 基本概念

简单说,代理模式就是找个 "中间人" 替目标对象干活 .

举个生活例子:你想租房,不直接找房东(目标对象),而是通过中介(代理对象)。中介会先帮你筛选房源、带看房子(预处理),你确定后中介再联系房东办手续(转发请求),最后还可能帮你交接房屋(事后处理),你全程不用直接对接房东,中介就起到了控制访问和辅助的作用。

java 复制代码
package user;

public class ProxyUser {
    private User realUser;
    public ProxyUser(User user){
        realUser = user;
    }
    public void borrowBook(String bookName){
        if(realUser instanceof NormalUser){
            ((NormalUser)realUser).borrowBook(bookName);
        }else if(realUser instanceof AdminUser){
            System.out.println("你没有权限借阅书籍,请以普通用户的方式借阅书籍");
        }
    }
    public void addBook(String bookName){
        if(realUser instanceof AdminUser){
            (( AdminUser)realUser).addBook(bookName);
        }else if(realUser instanceof NormalUser){
            System.out.println("你没有权限上架书籍,请以管理员用户的方式上架书籍");
        }
    }

}

4 关于jar包的使用

4.1 什么是jar包?

JAR(JavaArchive)包是⼀种⽤于打包Java应⽤程序和相关组件的⽂件格式。它基本上是⼀个ZIP

格式的压缩⽂件,但是专⻔⽤于Java平台。

4.2 项目引入jar包

1.打开项⽬,创建⼀个⽂件夹为:lib


下一步具体步骤如下:

• 点击File,选择"ProjectStru..."

• 在左侧⾯板中,选择"Libraries",点击"+"按钮,选择"Java"

• 浏览并选择你刚刚创建的JAR⽂件

测试导⼊是否成功:

导⼊jar包没有问题了!

二 图书管理系统

1.模块划分介绍

图书系统中模块的划分共划分为4块:⽤⼾模块、书籍相关模块、⼯具相关模块、常量值相关模块。我

们依次将4个模块在IDEA当中进⾏创建,供后续使⽤。

其中LibrarySystem模块为程序的⼊⼝。

2. User模块

从⻆⾊上我们将图书系统划分为管理员模块和普通⽤户模块,同时我们⽀持多普通⽤户进⾏登录。每个⽤户的属性包含:姓名、ID、⻆⾊3个属性。抽取AdminUser与NormalUser的共同属性与方法,将User类定义为抽象类.

2.1 User类

java 复制代码
package user;

public abstract class User {
    protected String name;  // 用户名
    protected int userID;   // 用户ID

    protected String role;  //管理员 普通用户

    public User(String name, int userID,String role) {
        this.name = name;
        this.userID = userID;
        this.role = role;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getUserID() {
        return userID;
    }

    public void setUserID(int userID) {
        this.userID = userID;
    }

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    public abstract int display();

}

2.2 AdminUser(role---管理员写死)

java 复制代码
package user;

import java.util.Scanner;

public class AdminUser extends User {
    public Scanner scanner = new Scanner(System.in);
    public AdminUser(String name, int userID) {
        super(name, userID,"管理员");
    }
    @Override
    public int display() {
        System.out.println("管理员 " + name + " 的操作菜单:");
        System.out.println("1. 查找图书 ");
        System.out.println("2. 打印所有的图书");
        System.out.println("3. 退出系统");
        System.out.println("4. 上架图书");
        System.out.println("5. 修改图书");
        System.out.println("6. 下架图书");
        System.out.println("7. 统计借阅次数");
        System.out.println("8. 查看最受欢迎的前K本书");
        System.out.println("9. 查看库存状态");
        System.out.println("10. 检查超过一年未下架的图书");
        System.out.println("请选择你的操作: ");
        return scanner.nextInt();

    }
    //上架图书
    public void addBook(String bookName) {
        System.out.println(" 管理员 " + name + " 上架了 " + bookName);
    }
    //图书修改 支持修改书名 作者 类别
    public void updateBook(){

    }
    //删除书籍
    public void removeBook(){

    }
    //查询每本书的借阅次数
    public void borrowCount(){

    }
    //查询最受欢迎的前n本书
    public void generateBook(){

    }
    //查看库存状态
    public void checkInventoryStatus(){

    }
    //移除上架超过一年的图书
    public void checkAndRemoveOldBooks(){

    }
    //退出系统
    public void exit(){

    }


}

2.3 NormalUser(role---普通用户写死)

java 复制代码
package user;

import java.util.Scanner;

public class NormalUser extends User{
    Scanner scanner = new Scanner(System.in);
    public NormalUser(String name, int userID) {
        super(name, userID,"普通用户");
    }
    private void loadBorrowedBook() {

    }
    private void storeBorrowedBook() {

    }
    @Override
    public int display() {
        System.out.println(" 普通⽤户 " + name + " 的操作菜单:");
        System.out.println("1. 查找图书 ");
        System.out.println("2. 打印所有的图书");
        System.out.println("3. 退出系统");
        System.out.println("4. 借阅图书");
        System.out.println("5. 归还图书");
        System.out.println("6. 查看当前个人借阅情况");
        System.out.println("请选择你的操作: ");
        return scanner.nextInt();
    }
    public void borrowBook(String bookName) {
        System.out.println(" 普通⽤户 " + name + " 借阅了 " + bookName);
    }

    //借阅图书
    public void borrowBook() {

    }
    //归还图书
    public void returnBook() {

    }
    // 查看个⼈借阅情况
    public void viewBorrowHistory() {

    }

}

2.4 引入工厂模式

在user包下创建factory包

1.创建⼯⼚接⼝

java 复制代码
package user.factory;

import user.User;

public interface IUserFactory {
    User createUser(String name, int ID);
}

2.创建具体的⼯厂

管理员工厂

java 复制代码
package user.factory;

import user.AdminUser;
import user.User;

public class AdminUserFactory implements IUserFactory{
    @Override
    public User createUser(String name, int ID) {
        return new AdminUser(name,ID);
    }
}

普通用户工厂

java 复制代码
package user.factory;

import user.NormalUser;
import user.User;

public class NormalUserFactory implements IUserFactory {
    @Override
    public User createUser(String name, int ID) {
        return new NormalUser(name,ID);
    }
}

修改LibrarySystem类

java 复制代码
import user.User;
import user.factory.AdminUserFactory;
import user.factory.IUserFactory;
import user.factory.NormalUserFactory;

public class LibrarySystem {
    public static void main(String[] args) {
        IUserFactory adminFactory = new AdminUserFactory();
        User adminUser = adminFactory.createUser(" 刘备 ", 1);

        IUserFactory normalFactory = new NormalUserFactory();
        User normalUser1 = normalFactory.createUser(" 关⽻ ", 2);
        User normalUser2 = normalFactory.createUser(" 张⻜ ",3);
    }
}

2.5 User模块-引⼊代理模式控制对象权限

java 复制代码
package user;

public class ProxyUser {
    private User realUser;
    public ProxyUser(User user){
        realUser = user;
    }

    public User getRealUser() {
        return realUser;
    }

   //调用菜单
    public int display(){
        return -1;

    }
    //添加书籍操作
    public void addBook(){

    }
    //更新实书籍操作
    public void updateBook(){

    }
    //移除书籍
    public void removeBook(){

    }
    //查看借阅次数
    public void borrowCount(){

    }
    //查看最受欢迎的前n本书
    public void generateBook(){

    }
    //查看库存状态
    public void checkInventoryStatus(){

    }
    //移除上架超过一年的书籍
    public void checkAndRemoveOldBooks(){

    }
    //...........................................普通相关法.................................................//
    //借阅图书
    public void borrowBook( ){

    }
    //归还图书
    public void returnBook(){

    }
    //查看个人借阅情况
    public void viewBorrowHistory(){

    }



}

LibrarySystem类中进⾏整合

java 复制代码
import user.ProxyUser;
import user.User;
import user.factory.AdminUserFactory;
import user.factory.IUserFactory;
import user.factory.NormalUserFactory;

public class LibrarySystem {
    public static void main(String[] args) {
        IUserFactory adminFactory = new AdminUserFactory();
        User adminUser = adminFactory.createUser(" 刘备 ", 1);

        IUserFactory normalFactory = new NormalUserFactory();
        User normalUser1 = normalFactory.createUser(" 关⽻ ", 2);
        User normalUser2 = normalFactory.createUser(" 张⻜ ",3);

        ProxyUser proxyUserAdmin = new ProxyUser(adminUser);
        ProxyUser proxyUserNormal1 = new ProxyUser(normalUser1);
        ProxyUser proxyUserNormal2 = new ProxyUser(normalUser2);



    }
}

3.book模块

3.1 Book类的定义

关于书籍类我们书籍的属性如下:

注意构造⽅法的实现对于某些字段可以不加⼊参数:

1.bookId【这⾥后续会进⾏累加/⾃增操作,此时在构造Book对象的时候,并不知道当前ID】

  1. isBorrowed 【任何⼀种书籍,默认都是未借出状态,此时可以不进⾏处理】

  2. borrowCount【借阅次数默认为0,此时可以不进⾏处理】

java 复制代码
package book;

import java.time.LocalDateTime;

public class Book implements Comparable<Book>{
    private int bookId; //书Id
    private String title; //书名
    private String author; //作者
    private String category; //类别
    private int publishYear; //出版年份
    private boolean isBorrowed; //借阅状态
    private int borrowCount; //借阅次数
    private LocalDateTime shelfDate; //上架日期

    public Book(String title, String author, String category, int publishYear, LocalDateTime shelfDate) {
        this.title = title;
        this.author = author;
        this.category = category;
        this.publishYear = publishYear;
        this.isBorrowed = false;
        this.borrowCount = 0;
        this.shelfDate = shelfDate;
    }

    public int getBookId() {
        return bookId;
    }

    public void setBookId(int bookId) {
        this.bookId = bookId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }

    public int getPublishYear() {
        return publishYear;
    }

    public void setPublishYear(int publishYear) {
        this.publishYear = publishYear;
    }

    public boolean isBorrowed() {
        return isBorrowed;
    }

    public void setBorrowed(boolean borrowed) {
        isBorrowed = borrowed;
    }

    public int getBorrowCount() {
        return borrowCount;
    }

    public void setBorrowCount(int borrowCount) {
        this.borrowCount = borrowCount;
    }

    public LocalDateTime getShelfDate() {
        return shelfDate;
    }

    public void setShelfDate(LocalDateTime shelfDate) {
        this.shelfDate = shelfDate;
    }
    public void incrementBorrowCount() {
        this.borrowCount++;
    }
    public void decreaseBorrowCount() {
        this.borrowCount--;
    }

    @Override
    public String toString() {
        return "Book{" +
                "bookId=" + bookId +
                ", title='" + title + '\'' +
                ", author='" + author + '\'' +
                ", category='" + category + '\'' +
                ", publishYear=" + publishYear +
                ", isBorrowed=" + isBorrowed +
                ", borrowCount=" + borrowCount +
                ", shelfDate=" + shelfDate +
                '}';
    }

    @Override
    public int compareTo(Book o) {
        return 0;
    }
}

3.2 Library类的定义

3.2.1 Library中的设计思路

我们期望书籍等数据最好可以进⾏持久化,所谓持久化就是将数据存储到MySQL、⽂件等介质中,不会因为断电等情况影响数据。否则每次运⾏程序数据都需要重新进⾏存储等⽐较繁琐。由于⽬前知识储备,我们这⾥会采⽤⽂件进⾏存储,把⽂件相关操作封装到jar包,借助已经写好的⼯具来进⾏操作。我们这⾥先把给定的jar包进⾏导⼊项⽬。接下来我们来看整体的设计流程。实例化Library-》加载⽂件当中的内容-》对⽂件进⾏操作-》将操作后的数据写⼊⽂件

3.2.2 AnalyzingBook 类实现⽂件内容的写⼊

在util包下创建AnalyzingBook 类:实现的功能如下

java 复制代码
public class AnalyzingBook {
    //给文件写数据
    public void storeObject(Book[] books, String filename){
      

    }
    //给文件读数据
    public Book[] loadObject(String filename){
        return null;

    }
    //把读出来的数据进行解析
    private Book parseBookJson(String json){
        return null;

   }
}

现有⼀组书籍进⾏⽂件的存储

java 复制代码
Book[] books = new Book[4];
books[0] = new Book("java", "zhangsan", " 编程 ", 1994, LocalDateTime.of(2023, 9, 24,12,3,1));
books[1] = new Book("mysql", "lisi", " 编程 ", 1999, LocalDateTime.of(2024, 2, 10,12,3,4));
books[2] = new Book("php", "wangwu", " 编程 ", 2020, LocalDateTime.of(2023, 9, 23,4,2,1));
books[3] = new Book(" 西游记 ", " 吴承恩 ", "⼩说 ", 2024,LocalDateTime.of(2023, 9,23,13,2,1));

当存⼊⽂件之后,⽂件⼀⾏代表⼀本书籍的书籍对象。写⼊书籍思路:

1.实现AnalyzingBook 类

  1. 先将书籍数组对象中的每个书籍对象进⾏字符串的序列化。

  2. 每本书籍与书籍之间使⽤\n进⾏换⾏分割

books代表书籍数组对象

filename 代表⽂件名称【如果不存在该⽂件,会⾃动创建该⽂件】

java 复制代码
public void storeObject(Book[] books, String filename){
        //需要先计算有多少本有效书籍
        int booksUserLen = 0;
        for(int i = 0;i < books.length;i++){
            if(books[i] != null){
                booksUserLen++;
            }
        }
        StringBuilder jsonArray = new StringBuilder();
        for(int i = 0;i < booksUserLen;i++){
            if(books[i] != null){
                jsonArray.append(books[i].toJSON());
                if(i != booksUserLen-1){
                    //一本书籍完成后以\n进行分割
                    jsonArray.append("\n");
                }
            }

        }
        //数据写入文件
        FileUtils.writeFile(jsonArray.toString(),filename);
}

注意:

• 上述代码当中toJSON需要我们⾃⼰实现

• FileUtils中的writeFile⽅法已经实现好,可以直接使用

在项目里新建allbooks文本文件

测试一下

java 复制代码
public static void main(String[] args) {
        Book[] books = new Book[4];
        books[0] = new Book("java", "zhangsan", " 编程 ", 1994, LocalDateTime.of(2023, 9, 24,12,3,1));
        books[1] = new Book("mysql", "lisi", " 编程 ", 1999, LocalDateTime.of(2024, 2, 10,12,3,4));
        books[2] = new Book("php", "wangwu", " 编程 ", 2020, LocalDateTime.of(2023, 9, 23,4,2,1));
        books[3] = new Book(" 西游记 ", " 吴承恩 ", "⼩说 ", 2024,LocalDateTime.of(2023, 9,23,13,2,1));

        AnalyzingBook analyzingBook = new AnalyzingBook();
        analyzingBook.storeObject(books, Constant.ALL_BOOK_FILE_NAME);
}
3.2.3 Book类的toJSON的实现

在Book类当中实现toJSON ⽅法,实现逻辑是将每个属性使⽤逗号进⾏拼接,存储到⽂件即可

java 复制代码
public String toJSON(){
        StringBuilder json = new StringBuilder();
        json.append(bookId).append(",");
        json.append(title).append(",");
        json.append(author).append(",");
        json.append(category).append(",");
        json.append(publishYear).append(",");
        json.append(isBorrowed).append(",");
        json.append(borrowCount).append(",");
        json.append(shelfDate != null ? shelfDate.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) : "null");
        //条件:shelfDate != null(检查日期是否为空)。
        //结果A(不为空时):shelfDate.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)。
        //这会将日期对象转换为标准格式字符串,例如 2023-10-27T14:30:00。
        //结果B(为空时):直接拼接字符串 "null"。
        return json.toString();
}
3.2.4 AnalyzingBook 类实现⽂件内容的读取

读取⽂件的每⼀⾏字符串,转化为书籍对象。

1.先读取⽂件当中的所有内容

  1. 使⽤\n作为分隔符进⾏字符串分割,⽅便得到每⼀个书籍对象对应的字符串

  2. 把对应字符串"组装"成书籍对象

java 复制代码
 public Book[] loadObject(String filename){
        String content = FileUtils.readFile(filename);//读取到内容的时候会自动在末尾加\n
        if(content == null || content.isEmpty()){
            System.out.println("File is empty or does not exist: " + filename);
            return null;
        }
        //2. 使用\n作为分隔符进行字符串分割
        String[] bookJsonStrings = content.split("\n");//如果分隔符出现在字符串的末尾,生成的末尾空字符串会被自动丢弃(忽略)

        //把字符串"组装"成书籍对象
        Book[] bookList = new Book[bookJsonStrings.length];
        for (int i = 0; i < bookJsonStrings.length; i++) {
            Book book = parseBookJson(bookJsonStrings[i]);
            bookList[i] = book;
        }
        return bookList;

}
java 复制代码
public static void main(String[] args) {
        AnalyzingBook analyzingBook = new AnalyzingBook();
        Book[] ret = analyzingBook.loadObject(Constant.ALL_BOOK_FILE_NAME);
        for (int i = 0; i < ret.length; i++) {
            System.out.println(ret[i]);
        }

}

parseBookJson

⽅法实现

1.检查json是否是空的

  1. 使⽤逗号分割出每个属性的值

  2. 每个属性进⾏转化赋值

  3. 构造书籍对象

java 复制代码
private Book parseBookJson(String json){
        //1.检查json是否是空的
        //加这个方法防止:
        //如果文件损坏,只有一堆换行符。
        //如果有人手动编辑了文件,不小心敲了回车。
        if(json.isEmpty()) {
            return null;
        }
        //2.使⽤逗号分割出每个属性的值
        String[] pairs = json.split(",");
        //3.每个属性进⾏转化赋值
        int bookId = Integer.parseInt(pairs[0]);
        String title = pairs[1];
        String author = pairs[2];
        String category = pairs[3];
        int publishYear = Integer.parseInt(pairs[4]);
        boolean isBorrowed = Boolean.parseBoolean(pairs[5]);
        int borrowCount = Integer.parseInt(pairs[6]);
        LocalDateTime shelfDate = LocalDateTime.parse(pairs[7]);
        //构造书籍对象
        if (title != null && author != null && category != null && shelfDate != null) {
            Book book = new Book(title, author, category, publishYear, shelfDate);
            book.setBorrowed(isBorrowed);
            book.setBorrowCount(borrowCount);
            book.setBookId(bookId);
            return book;
        }
        return null;

}
3.2.5 Library⽂件&读取存储具体细节设计
3.2.5.1 ⽂件内容读取到内存中进⾏存储

既然我们要把解析的数据存储到内存当中,那么我们需要在内存当中定义⼀个数组来存储读取到的书籍对象。
定义Library类

java 复制代码
public class Library {
    private Book[] books;//当前图书数组
    private int bookCount;//实际存储的图书数量
    private final AnalyzingBook analyzingBook = new AnalyzingBook();//书籍解析相关类
    public Library() {
       loadAllBook();

    }
}

那么问题来了,books数组初始化多⼤合适呢?我们的思路是:

1.默认数组容量给⼀个默认值,⽐如5

  1. 然后根据实际读取到的⽂件内容,来确定⼤⼩

这个默认值是⼀个常量,我们在constant包下定义一个常量类Constant来存储这些变量

java 复制代码
public class Constant {
    //内存中的书籍数组初识容量
    public static final int CAPACITY = 5;
}

代码实现

java 复制代码
private void loadAllBook(){
        //1.读取文件内容
        Book[] allBook = analyzingBook.loadObject(Constant.ALL_BOOK_FILE_NAME);
        //2.默认大小为5
        books = new Book[Constant.CAPACITY];
        //3.判断是否是有数据 没有有效书籍为0
        if(allBook == null){
            bookCount = 0;
        }else{
            //查看实际书籍大小为多少 是否大于默认的长度5
            int allBookLen = allBook.length;
            //大于默认长度 books数组分配实际的大小
            if(allBookLen > books.length){
                books = new Book[allBook.length];
            }
            //把读到的元素进行赋值
            for (int i = 0; i < allBookLen; i++) {
                books[i] = allBook[i];
            }
            //修改实际有效书籍个数
            bookCount = allBookLen;
        }
}
3.2.5.2 内存中的书籍数组进⾏存储到⽂件
java 复制代码
private void storeBook() {
        analyzingBook.storeObject(books,Constant.ALL_BOOK_FILE_NAME);

}

常量类添加常量:Constant.ALL_BOOK_FILE_NAME

java 复制代码
public class Constant {
    //内存中的书籍数组初识容量
    public static final int CAPACITY = 5;
    //存储所有图书的⽂件
    public static final String ALL_BOOK_FILE_NAME = "allbook.txt";
}

4业务操作逻辑框架分解

接下来我们需要完成业务逻辑的实现,⾸先我们需要对框架进⾏梳理,在正式梳理之前,我们需要完

善遗留⼯作:根据⽤户不同选择进⼊不同等级的⽤户⻆⾊下
之前的LibrarySystem类

java 复制代码
import user.ProxyUser;
import user.User;
import user.factory.AdminUserFactory;
import user.factory.IUserFactory;
import user.factory.NormalUserFactory;

public class LibrarySystem {
    public static void main(String[] args) {
        IUserFactory adminFactory = new AdminUserFactory();
        User adminUser = adminFactory.createUser(" 刘备 ", 1);

        IUserFactory normalFactory = new NormalUserFactory();
        User normalUser1 = normalFactory.createUser(" 关⽻ ", 2);
        User normalUser2 = normalFactory.createUser(" 张⻜ ",3);

        ProxyUser proxyUserAdmin = new ProxyUser(adminUser);
        ProxyUser proxyUserNormal1 = new ProxyUser(normalUser1);
        ProxyUser proxyUserNormal2 = new ProxyUser(normalUser2);



    }
}

此时的proxyUserAdmin、proxyUserNormal1、proxyUserNormal2,是我们的代理对象,我们需要根据⽤户的选择,来确定最终的⻆⾊。

在LibrarySystem类中实现selectProxyRole⽅法:

java 复制代码
public static ProxyUser selectProxyRole(ProxyUser  proxyUserAdmin,ProxyUser proxyUserNormal1,ProxyUser proxyUserNormal2){
        System.out.println("选择角色进行登录: ");
        System.out.println("1.管理员\n2.普通用户(关羽)\n3.普通用户(张飞)\n4.退出系统");
        ProxyUser currentUser = null;
        Scanner scanner = new Scanner(System.in);
        int choice = scanner.nextInt();
        switch(choice){
            case 1:
                currentUser = proxyUserAdmin;
                break;
            case 2:
                currentUser = proxyUserNormal1;
                break;
            case 3:
                currentUser = proxyUserNormal2;
                break;
            case 4:
            //在 Java 中,System.exit(0); 是一个非常直接且强力的指令,它的核心作用是强制终止当前正在运行的 Java 虚拟机(JVM)。
            //0 代表"正常退出":表示程序已经完成了它的任务,没有发生任何错误。
            //非 0(如 1, -1, 2 等)代表"异常退出":表示程序因为某种错误或异常情况而不得不停止。
                System.out.println("系统已退出..");
                System.exit(0);
                break;
        }
        return currentUser;
}

完善LibrarySystem类:

java 复制代码
public static void main(String[] args) {
        IUserFactory adminFactory = new AdminUserFactory();
        User adminUser = adminFactory.createUser(" 刘备 ", 1);

        IUserFactory normalFactory = new NormalUserFactory();
        User normalUser1 = normalFactory.createUser(" 关⽻ ", 2);
        User normalUser2 = normalFactory.createUser(" 张⻜ ",3);

        ProxyUser proxyUserAdmin = new ProxyUser(adminUser);
        ProxyUser proxyUserNormal1 = new ProxyUser(normalUser1);
        ProxyUser proxyUserNormal2 = new ProxyUser(normalUser2);

        //选择对应⻆⾊进⾏登录
        ProxyUser currentUser = selectProxyRole(proxyUserAdmin,proxyUserNormal1,proxyUserNormal2);

        while(true){
            int choice = currentUser.getRealUser().display();
            //此时⽆需关心是管理员还是普通⽤户,代理类会做权限判断
            currentUser.handleOperation(choice);


        }
}

handleOperation ⽅法会在ProxyUser类中进行实现

业务框架搭建

整个业务逻辑从ProxyUser 类中进⾏管理触发,整个框架为:

在utils包下定义一个异常类

java 复制代码
package utils;

public class PermissionException extends RuntimeException{
    public PermissionException() {
    }

    public PermissionException(String message) {
        super(message);
    }
}

ProxyUser类完善业务逻辑框架

ProxyUser 类添加⽅法:普通用户权限检查方法和管理员权限检查方法

• 每个业务需要判断该代理对象是否是有权限的

• 调⽤对应⽤户当中的⽅法进⾏操作

• 注意:查找图书,显⽰图书,退出系统是2个⻆⾊都有的⽅法,这些⽅法可以提出到Library当中,不需要区分

java 复制代码
//管理员用户权限检查方法
private void checkRealUserWhetherAdminUser(String msg) {
        if(!(realUser instanceof AdminUser)) {
            //⾃定义异常
            throw new PermissionException(msg);
        }
}
//普通用户检查方法
private void checkRealUserWhetherNormalUser(String msg) {
        if(!(realUser instanceof NormalUser)){
            throw new PermissionException(msg);
        }
}

handleOperation ⽅法,根据选择进⾏对应操作

查找图书、显⽰图书、退出系统三种操作对于管理员和普通⽤户来说都是共⽤的,所以直接定义在Library当中
先定义框架

java 复制代码
public void handleOperation(int choice) {
        if(realUser instanceof AdminUser){
            switch(choice){
                case 1:
                    break;
                case 2:
                    break;
                case 3:
                    break;
                case 4:
                    break;
                case 5:
                    break;
                case 6:
                    break;
                case 7:
                    break;
                case 8:
                    break;
                case 9:
                    break;
                case 10:
                    break;
                default:
                    break;

            }

        }else if(realUser instanceof NormalUser){
            switch(choice){
                case 1:
                    break;
                case 2:
                    break;
                case 3:
                    break;
                case 4:
                    break;
                case 5:
                    break;
                case 6:
                    break;
                default:
                    break;

            }

        }

}

继续完善Constant 类:

java 复制代码
package constant;

public class Constant {
    //内存中的书籍数组初识容量
    public static final int CAPACITY = 5;
    //存储所有图书的⽂件
    public static final String ALL_BOOK_FILE_NAME = "allbooks.txt";

    //------------------管理员相关操作管理----------------//
    //查找图书
    public static final int SEARCH_BOOK = 1;
    //显⽰图书
    public static final int DISPLAY_BOOK = 2;
    //退出系统
    public static final int EXIT = 3;
    //上架图书
    public static final int ADD_BOOK = 4;
    //更新图书
    public static final int UPDATE_BOOK = 5;
    //删除图书
    public static final int REMOVE_BOOK = 6;
    //查看图书的借阅次数
    public static final int BORROWED_BOOK_COUNT = 7;
    //查看受欢迎的图书
    public static final int GENERATE_BOOK = 8;
    //查看库存状态
    public static final int CHECK_INVENTORY_STATUS = 9;
    //移除上架超过1年的书籍
    public static final int CHECK_AND_REMOVE_OLD_BOOK = 10;

    //------------------普通⽤⼾相关操作管理----------------//
    //借阅图书
    public static final int BORROWED_BOOK = 4;
    //归还图书
    public static final int RETURN_BOOK = 5;
    //查看个⼈借阅情况
    public static final int VIEW_BORROW_HISTORY_BOOK = 6;


}

123这种也被称为magicnumber(幻数),缺点在于如果只是使⽤数字,别的程序员可能并不

能读懂甚⾄过⼀段时间,⾃⼰也可能看不懂。所以定义为常量可以增加代码的可读性。

java 复制代码
public void handleOperation(int choice) {
        if(realUser instanceof AdminUser){
            switch(choice){
                case Constant.SEARCH_BOOK:
                    break;
                case Constant.DISPLAY_BOOK:
                    break;
                case Constant.EXIT:
                    break;
                case Constant.ADD_BOOK:
                    break;
                case Constant.UPDATE_BOOK:
                    break;
                case Constant.REMOVE_BOOK:
                    break;
                case Constant.BORROWED_BOOK_COUNT:
                    break;
                case Constant.GENERATE_BOOK:
                    break;
                case Constant.CHECK_INVENTORY_STATUS:
                    break;
                case Constant.CHECK_AND_REMOVE_OLD_BOOK:
                    break;
                default:
                    break;

            }

        }else if(realUser instanceof NormalUser){
            switch(choice){
                case Constant.SEARCH_BOOK:
                    break;
                case Constant.DISPLAY_BOOK:
                    break;
                case Constant.EXIT:
                    break;
                case Constant.BORROWED_BOOK:
                    break;
                case Constant.RETURN_BOOK:
                    break;
                case Constant.VIEW_BORROW_HISTORY_BOOK:
                    break;
                default:
                    break;

            }

        }
}

在Library类中添加查找图书,显示图书和退出系统方法

java 复制代码
public void searchBook(){
       System.out.println("查找图书");
}
public void disPlayBook(){
       System.out.println("显示书籍");
}
public void exit(){
       System.out.println("退出系统");
}

在ProxyUser类中添加成员变量,以便使用Library类的查找图书,显示图书和退出系统方法(后文会将Library类设计成单例模式)

java 复制代码
private Library library = new Library();

完善handleOperation方法

java 复制代码
public void handleOperation(int choice) {
        if(realUser instanceof AdminUser){
            switch(choice){
                case Constant.SEARCH_BOOK:
                    library.searchBook();
                    break;
                case Constant.DISPLAY_BOOK:
                    library.disPlayBook();
                    break;
                case Constant.EXIT:
                    library.exit();
                    break;
                case Constant.ADD_BOOK:
                    addBook();
                    break;
                case Constant.UPDATE_BOOK:
                    updateBook();
                    break;
                case Constant.REMOVE_BOOK:
                    removeBook();
                    break;
                case Constant.BORROWED_BOOK_COUNT:
                    borrowCount();
                    break;
                case Constant.GENERATE_BOOK:
                    generateBook();
                    break;
                case Constant.CHECK_INVENTORY_STATUS:
                    checkInventoryStatus();
                    break;
                case Constant.CHECK_AND_REMOVE_OLD_BOOK:
                    checkAndRemoveOldBooks();
                    break;
                default:
                    System.out.println("⽆效的操作。 ");
                    break;

            }

        }else if(realUser instanceof NormalUser){
            switch(choice){
                case Constant.SEARCH_BOOK:
                    library.searchBook();
                    break;
                case Constant.DISPLAY_BOOK:
                    library.disPlayBook();
                    break;
                case Constant.EXIT:
                    library.exit();
                    break;
                case Constant.BORROWED_BOOK:
                    borrowBook();
                    break;
                case Constant.RETURN_BOOK:
                    returnBook();
                    break;
                case Constant.VIEW_BORROW_HISTORY_BOOK:
                    viewBorrowHistory();
                    break;
                default:
                    System.out.println("⽆效的操作。 ");
                    break;

            }

        }
}

在AdminUser类中加Libray类对象(后文会将Library类设计成单例模式)

java 复制代码
public Library library = new Library();

完善AdminUser类中的addBook方法

java 复制代码
public void addBook() {
        scanner.nextLine();//如果不加这行,直接调用下面的 nextLine(),程序可能会直接跳过书名输入,因为它会读取到上一次操作留下的回车符。
        System.out.println(" 请输⼊书名: ");
        String title = scanner.nextLine();  //输⼊书名
        System.out.println(" 请输⼊作者: ");
        String author = scanner.nextLine();  //输⼊作者
        System.out.println(" 请输⼊类别: ");
        String category = scanner.nextLine();  //输⼊图书类别
        System.out.println(" 请输⼊出版年份: ");
        int year = scanner.nextInt();  //输⼊出版年份    //scanner.nextInt() 只会读取整数部分。风险点:它不会读取末尾的回车符,回车符会残留在缓冲区里。
        scanner.nextLine();  //吞掉换⾏符
        LocalDateTime shelfDate = LocalDateTime.now();  //当前时间作为上架时间
        Book newBook = new Book(title, author, category, year, shelfDate);//创建新书对象
        //调⽤图书类添加图书
        library.addBook(newBook);
    }

在Library类中添加addBook方法

java 复制代码
public void addBook(Book book) {
        System.out.println("上架书籍....");
}

单例模式设计Library类

确保Library类只有一个实例

把构造方法访问修饰符改为privare,在Library类添加

java 复制代码
private static Library library;
public static Library getLibrary() {
        if(library == null){
            library = new Library();
        }
        return library;
}

修改用到使用到Library的地方

java 复制代码
private Library library = Library.getLibrary();//AdminUser类改为这个
private Library library = Library.getLibrary();//ProxyUser类改为这个

完善ProxyUser类中的addBook方法

java 复制代码
public void addBook(){
        checkRealUserWhetherNormalUser("普通用户没有权限上架书籍");
        ((AdminUser)realUser).addBook();
}

NormalUser 类完善业务逻辑框架

在设计该类的时候,我们需要想清楚⼀个逻辑。

• ⽤户借书怎么设计?

• 多个⽤户借书怎么设计?

• 借书之后存储到⽂件当中的格式是什么?

PairOfUidAndBookId 类设计

我们的设计是这样的:

• ⽂件当中存储借阅书籍信息为:⽤户ID,书籍ID的形式,每⼀⾏是⼀个借阅信息

1,101 :表⽰ID为1的⽤户借阅了ID为101的书籍

2,111

1,301

所以我们就涉及到了读取⽂件后,内存当中需要存储这些借阅信息。我们定义类PairOfUidAndBookId 来表⽰⼀条借阅信息,多条信息使⽤数组即可。

在book包当中定义

java 复制代码
package book;

public class PairOfUidAndBookId {
    private int userId;
    private int bookId;
    
    public PairOfUidAndBookId(int userId, int bookId) {
        this.userId = userId;
        this.bookId = bookId;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userID) {
        this.userId = userID;
    }

    public int getBookId() {
        return bookId;
    }

    public void setBookId(int bookId) {
        this.bookId = bookId;
    }
    //把对象序列化为JSON字符串的形式
    public String toJson() {
        StringBuilder json = new StringBuilder();
        json.append(userId).append(",");
        json.append(bookId);
        return json.toString();
    }
}

AnalyzingBorrowedBook类的设计

util包中定义

该设计⽅案和AnalyzingBook设计是类似的

java 复制代码
package utils;

import book.Book;
import book.PairOfUidAndBookId;
import com.bit.utils.FileUtils;

public class AnalyzingBorrowedBook {
    /**
     * 存到文件当中
     * @param pairOfUidAndBookIds
     * @param filename
     */
    public void storeObject(PairOfUidAndBookId[] pairOfUidAndBookIds, String filename){
        //先遍历pairOfUidAndBookIds数组当中不为空的数据多少个?
        int booksUseLen = 0;
        for (int i = 0; i < pairOfUidAndBookIds.length; i++) {
            if(pairOfUidAndBookIds[i] != null) {
                booksUseLen++;
            }
        }
        StringBuilder jsonArray = new StringBuilder();
        for(int i = 0;i < booksUseLen;i++){
            if(pairOfUidAndBookIds[i] != null){
                jsonArray.append(pairOfUidAndBookIds[i].toJSON());
                if(i != booksUseLen-1){
                    //一本书籍完成后以\n进行分割
                    jsonArray.append("\n");
                }
            }

        }
        FileUtils.writeFile(jsonArray.toString(),filename);
    }
    
    public PairOfUidAndBookId[] loadObject(String filename){
        String content = FileUtils.readFile(filename);
        if(content == null || content.isEmpty()){
            System.out.println("已借阅书籍列表无数据,表示用户没有借阅过书籍");
            return null;
        }
        //2. 使用\n作为分隔符进行字符串分割
        String[] JsonStrings = content.split("\n");

        PairOfUidAndBookId[] pairOfUidAndBookIds = new PairOfUidAndBookId[JsonStrings.length];
        for (int i = 0; i < JsonStrings.length; i++) {
            PairOfUidAndBookId pairOfUidAndBookId = new PairOfUidAndBookId();
            String[] uidAndBookIds = JsonStrings[i].split(",");
            pairOfUidAndBookId.setUserId(Integer.parseInt(uidAndBookIds[0]));
            pairOfUidAndBookId.setBookId(Integer.parseInt(uidAndBookIds[1]));
            pairOfUidAndBookIds[i] = pairOfUidAndBookId;

        }
        return pairOfUidAndBookIds;

    }
}

在PairOfUidAndBookId类中添加无参构造方法

java 复制代码
public PairOfUidAndBookId(){

}

在项目里添加borrowedbook文本文件

在Content类中添加

java 复制代码
public static final String BORROWED_BOOK_FILE_NAME = "borrowedbook.txt";

测试一下storeObject方法

java 复制代码
public static void main(String[] args) {
        PairOfUidAndBookId[] pairOfUidAndBookIds = new PairOfUidAndBookId[2];
        pairOfUidAndBookIds[0] = new PairOfUidAndBookId(1,1001);
        pairOfUidAndBookIds[1] = new PairOfUidAndBookId(2,1002);

        AnalyzingBorrowedBook analyzingBorrowedBook = new AnalyzingBorrowedBook();
        analyzingBorrowedBook.storeObject(pairOfUidAndBookIds, Constant.BORROWED_BOOK_FILE_NAME);
}

在PairOfUidAndBookId类中添加toString方法

java 复制代码
@Override
public String toString() {
        return "PairOfUidAndBookId{" +
                "userId=" + userId +
                ", bookId=" + bookId +
                '}';
}

测试一下loadObject方法

java 复制代码
public static void main(String[] args) {
        AnalyzingBorrowedBook analyzingBorrowedBook = new AnalyzingBorrowedBook();
        PairOfUidAndBookId[] ret = analyzingBorrowedBook.loadObject(Constant.BORROWED_BOOK_FILE_NAME);
        for (int i = 0; i < ret.length; i++) {
            System.out.println(ret[i]);
        }

}

在NormalUser类中添加成员变量

java 复制代码
 // ⽤户已借阅的图书相关信息
 private PairOfUidAndBookId[] pairOfUidAndBookIds;
 //当前书籍的借阅量
 private int borrowedCount;
 
 private final AnalyzingBorrowedBook analyzingBorrowedBook = new AnalyzingBorrowedBook();

在Constant类中添加

java 复制代码
//最多借阅的图书数量
public  static final int BORROW_BOOK_MAX_NUM = 5;

在NormalUser类中完善loadBorrowedBook方法

java 复制代码
private void loadBorrowedBook() {
        //1.读取文件内容
        PairOfUidAndBookId[] allBorrowedBook = analyzingBorrowedBook.loadObject(Constant.BORROWED_BOOK_FILE_NAME);
        //2.默认大小为5
        pairOfUidAndBookIds = new PairOfUidAndBookId[Constant.BORROW_BOOK_MAX_NUM];
        //3.判断是否是有数据 没有有效书籍为0
        if(allBorrowedBook == null){
            borrowedCount = 0;
        }else{
            //查看实际书籍大小为多少 是否大于默认的长度5
            int allBorrowedBookLen = allBorrowedBook.length;
            //大于默认长度 books数组分配实际的大小
            if( allBorrowedBookLen > pairOfUidAndBookIds.length){
                pairOfUidAndBookIds = new PairOfUidAndBookId[allBorrowedBookLen];
            }
            //把读到的元素进行赋值
            for (int i = 0; i < allBorrowedBookLen; i++) {
                pairOfUidAndBookIds[i] = allBorrowedBook[i];
            }
            //修改实际有效书籍个数
            borrowedCount = allBorrowedBookLen;
        }

}

在NormalUser类中完善storeBorrowedBook方法

java 复制代码
private void storeBorrowedBook() {
        analyzingBorrowedBook.storeObject(pairOfUidAndBookIds,Constant.BORROWED_BOOK_FILE_NAME);
}

修改NormalUser类的构造方法(调构造方法时调用loadBorrowedBook)

java 复制代码
public NormalUser(String name, int userID) {
        super(name, userID,"普通用户");
        loadBorrowedBook();
}

5 管理员端业务实现

添加书籍

检查权限是否合法-》调⽤管理员内部添加⽅法准备数据-》调⽤Libary⽅法进⾏业务具体实现

在前面这个我们已经实现了前两步

我们现在实现Library类的addBook方法

java 复制代码
public void addBook(Book book) {
        if(bookCount >= books.length) {
            System.out.println(" 图书馆已满,⽆法上架更多图书! ");
            //TODO:扩容
            return;
        }
        books[bookCount] = book;//将书籍对象存入数组
        //当前书籍为第一本书
        if(bookCount == 0){
            book.setBookId(1);
        }else{
            Book bookLast = books[bookCount - 1];
            book.setBookId(bookLast.getBookId() + 1);
        }
        bookCount++;
        storeBook();
        System.out.println(" 图书 "+book.getTitle()+" 上架成功! ");
}

我们在LibrarySystem类中测试一下

更新书籍

1.ProxyUser类:

java 复制代码
public void updateBook(){
        checkRealUserWhetherAdminUser("普通用户没有权限更新书籍");
        ((AdminUser)realUser).updateBook();
}

完成上面这一步我们要完善Library类的disPlay方法还有添加searchById方法

java 复制代码
public void disPlayBook(){
        for (int i = 0; i < bookCount; i++) {
            System.out.println(books[i]);
        }
}
java 复制代码
public Book searchById(int bookId){
        loadAllBook();
        for (int i = 0; i < bookCount; i++) {
            if(books[i].getBookId() == bookId){
                return books[i];
            }
        }
        return null;
}

2.AdminUser类:

java 复制代码
 public void updateBook(){
        System.out.println("目前所有书籍信息如下: ");
        library.disPlayBook();
        System.out.println(" 请输⼊要修改的图书 id: ");
        int bookId = scanner.nextInt();
        //吞掉换⾏符
        scanner.nextLine();
        //获取对应的图书
        Book book = library.searchById(bookId);
        if(book == null) {
            System.out.println(" 没有 Id 为: "+bookId+" 的书籍! ");
            return;
        }
        System.out.println(" 当前书名: " + book.getTitle());
        System.out.println(" 请输⼊新的书名: ");
        String newTitle = scanner.nextLine();  //输⼊新的书名
        System.out.println(" 当前作者: " + book.getAuthor());
        System.out.println(" 请输⼊新的作者: ");
        String newAuthor = scanner.nextLine();  //输⼊新的作者
        System.out.println(" 当前类别: " + book.getCategory());
        System.out.println(" 请输⼊新的类别: ");
        String newCategory = scanner.nextLine();  //输⼊新的类别
        //更新对应书籍的信息
        book.setTitle(newTitle);
        book.setAuthor(newAuthor);
        book.setCategory(newCategory);
        library.updateBook(book);

}

Library类

java 复制代码
public void updateBook(Book book) {
        //⼀定要进⾏存储
        storeBook();
        System.out.println("更新书籍成功,更新后书籍信息如下: ");
        System.out.println(book);
}

我们完成一下Library类的exit方法

java 复制代码
public void exit(){
       System.exit(0);
}

移除(下架)书籍

ProxyUser类

java 复制代码
 public void removeBook(){
       checkRealUserWhetherNormalUser("普通用户没有权限移除书籍");
       ((AdminUser)realUser).removeBook();
 }

AdminUser类

java 复制代码
public void removeBook(){
        System.out.println("目前所有书籍信息如下: ");
        library.disPlayBook();
        System.out.println(" 请输⼊要移除的图书的 Id: ");
        int bookId = scanner.nextInt();
        scanner.nextLine();  //吞掉换⾏符
        //记录⼀下删除的图书对象
        Book removeBook = library.searchById(bookId);
        if(removeBook == null){
            System.out.println("没有你要移除的图书");
            return;
        }
        //开始删除
        library.removeBook(bookId);
}

Library类

java 复制代码
public void removeBook(int bookId) {
        int index = searchByIdReturnIndex(bookId);//开始删除从当前位置的后边往前移动
        if(index == -1){
            System.out.println("没有你要删除的书籍");
            return;
        }
        //bookCount-1防止越界
        for (int i = index; i < bookCount-1; i++) {
            books[i] = books[i+1];
        }
        books[bookCount-1] = null;//把最后一个元素置为null
        storeBook();
        bookCount--;
}

在Library类中添加searchByIdReturnIndex方法

java 复制代码
private int searchByIdReturnIndex(int bookId) {
        loadAllBook();
        for (int i = 0; i < bookCount; i++) {
            Book book = books[i];
            if(book.getBookId() == bookId) {
                return i;
            }
        }
        return -1;
}

图示

查看图书借阅次数

ProxyUser类

java 复制代码
public void borrowCount(){
        checkRealUserWhetherAdminUser("普通用户没有权限查看借阅次数");
        ((AdminUser)realUser).borrowCount();

}

AdminUser

java 复制代码
public void borrowCount(){
        library.borrowCount();
}

Library类

java 复制代码
public void borrowCount() {
        loadAllBook();
        for (int i = 0; i < bookCount; i++) {
           Book book = books[i];
           System.out.println("书籍名称 " + book.getTitle() + " 借阅次数 " + book.getBorrowCount());
        }
}

查看最受欢迎的前K本书

ProxyUser类

java 复制代码
 public void generateBook(){
        checkRealUserWhetherAdminUser("普通用户没有权限查看最受欢迎的前k本书");
        ((AdminUser)realUser).generateBook();
}

我们要比较最受欢迎的书,所以我们采用比较借阅次数从大到小,在Book类中的compareTo方法写入比较方式

java 复制代码
public int compareTo(Book o) {
       return o.borrowCount - this.borrowCount;
}

在Library类中添加getBookCount方法

java 复制代码
public int getBookCount() {
        return bookCount;
}

AdminUser类

java 复制代码
public void generateBook(){
        System.out.println(" 请输⼊你要查看的最受欢迎的前 K 本书 , 注意 k 值不能超 过: "+library.getBookCount());
        int k = scanner.nextInt();
        if(k <= 0 || k > library.getBookCount()) {
            System.out.println(" 没有最受欢迎的前 "+k+" 本书!");
            return;
        }
        library.generateBook(k);
}

Library类

在Library类中添加generateBook方法

java 复制代码
public void generateBook(int k) {
        loadAllBook();
        //把所有的书放在临时数据
        Book[] tmp = new Book[getBookCount()];
        for (int i = 0; i < getBookCount(); i++) {
            tmp[i] = books[i];
        }

        Arrays.sort(tmp);

        //把前k本书拷贝到数组
        Book[] generateBook = new Book[k];

        for (int i = 0 ;i < k; i++){
            generateBook[i] = tmp[i];
        }

        System.out.println("最受欢迎的前K本书如下: ");
        for (int i = 0; i < k; i++) {
            Book book = generateBook[i];
            System.out.println("书名 " + book.getTitle() + " 作者 " + book.getAuthor() + " 借阅次数 " + book.getBorrowCount());
        }

}

查看库存状态

ProxyUser类:

java 复制代码
public void checkInventoryStatus(){
        checkRealUserWhetherAdminUser("普通用户没有权限查看查看库存状态");
        ((AdminUser)realUser).checkInventoryStatus();
}

AdminUser

java 复制代码
public void checkInventoryStatus(){
        library.checkInventoryStatus();
}

在Library类添加checkInventoryStatus方法

java 复制代码
public void checkInventoryStatus() {
     loadAllBook();
     System.out.println("=============================");
     for (int i = 0; i < bookCount; i++) {
            Book book = books[i];
            String status = book.isBorrowed() ? "已借出" : "在馆";
            System.out.println(" 书名: "+book.getTitle()+"⽬前状态: "+status);
     }
     System.out.println("=================================");
}
    

移除上架超过1年的书籍

ProxyUser类:

java 复制代码
public void checkAndRemoveOldBooks(){
        checkRealUserWhetherAdminUser("普通用户没有权限移除上架超过一年的书籍");
        ((AdminUser)realUser).checkAndRemoveOldBooks();
}

AdminUser类

java 复制代码
public void checkAndRemoveOldBooks(){
        library.checkAndRemoveOldBooks();
}

Library类

在Library类中添加Scanner对象

java 复制代码
public Scanner scanner = new Scanner(System.in);

在Libray类中添加checkAndRemoveOldBooks方法

java 复制代码
public void checkAndRemoveOldBooks(){
        //确保操作的是最新数据
        loadAllBook();
        //获取当前时间戳
        long currentTimestamp = System.currentTimeMillis();//获取当前距离 1970 年的毫秒数
        // 将当前时间戳为转换为 LocalDateTime   将毫秒数转为瞬时时间点             关联本地时区                     最终转为不含时区的日期时间格式
        LocalDateTime currentDate = Instant.ofEpochMilli(currentTimestamp).atZone(ZoneId.systemDefault()).toLocalDateTime();

        boolean flg = false;//标记位,用来记录是否找到了满足条件的旧书

        for (int i = 0; i < bookCount; i++) {
            Book book = books[i];
            //获取当前书籍的上架时间
            LocalDateTime specifiedDate = book.getShelfDate();
            //计算两个日期之间的差值(以年为单位)
            long yearsBetween = ChronoUnit.YEARS.between(specifiedDate,currentDate);
            if(yearsBetween >= 1){
                System.out.println("图书 " + book.getTitle() + " 已经上架一年,是否移除?(y/n): ");
                String response = scanner.nextLine();// 读取用户的输入
                if(response.equalsIgnoreCase("y")){
                    //确认删除调用remove方法进行删除
                    removeBook(book.getBookId());
                    i--;//因为后面的书已经向前移动所以要重新检查当前索引位置
                }
                flg = true;// 标记找到了旧书
            }
        }
        if(!flg){
            System.out.println("没有上架超过一年的书籍! ");
        }else{
            System.out.println("删除成功");
        }
        storeBook();
}

6 普通⽤户端业务实现

借阅图书

ProxyUser类

java 复制代码
public void borrowBook( ){
        checkRealUserWhetherNormalUser("管理员用户没有权限借阅书籍");
        ((NormalUser)realUser).borrowBook();

}

NormalUser类

在NormalUser类中添加

java 复制代码
private Library library;

把构造方法改为

java 复制代码
public NormalUser(String name, int userID) {
        super(name, userID,"普通用户");
        library = Library.getLibrary();
        loadBorrowedBook();
}
java 复制代码
public void borrowBook() {
        System.out.println("目前书籍信息如下: ");
        library.disPlayBook();
        System.out.println("请输入你要借阅的书籍Id: ");
        int bookId = scanner.nextInt();

        //1.检查如果没有书籍,就不能借阅
        if(library.getBookCount() == 0){
            System.out.println("书架没有书籍不能借阅");
            return;
        }

        //加载已借阅书籍信息
        loadBorrowedBook();

        //判断要借阅的书 是否存在
        Book book = library.searchById(bookId);
        if(book == null){
            System.out.println("没有该Id的相关书籍: " +bookId);
            return;
        }
        //加载 是否当前要借阅的书籍 之前被自己 或者 别人借阅过
        for (int i = 0; i < borrowedCount; i++) {
            PairOfUidAndBookId pairOfUidAndBookId = pairOfUidAndBookIds[i];
            //找到对应的书籍
            if(pairOfUidAndBookId.getBookId() == book.getBookId()){
                if(getUserID() == pairOfUidAndBookId.getUserId()){
                    System.out.println("该书已经被你借阅过了,你的ID是: " +getUserID());
                    return;
                }else{
                    System.out.println("该书已经被人借阅过了.他的Id是: " + pairOfUidAndBookId.getUserId());
                    return;
                }

            }

        }
        library.borrowBook(bookId);

        //封装对象写到借阅表当中
        PairOfUidAndBookId pairOfUidAndBookId = new PairOfUidAndBookId(userID, book.getBookId());
        pairOfUidAndBookIds[borrowedCount] = pairOfUidAndBookId;
        borrowedCount++;
        //存储借阅图书
        storeBorrowedBook();
        System.out.println(" 借阅成功! ");

}

Library类

添加borrowBook方法

java 复制代码
public void borrowBook(int bookId) {
        loadAllBook();
        for (int i = 0; i < getBookCount(); i++) {
            Book book = books[i];
            if(book.getBookId() == bookId) {
                book.setBorrowed(true);
                book.incrementBorrowCount();
            }
        }
        storeBook();
}

归还图书

ProxyUser类

java 复制代码
public void returnBook(){
        checkRealUserWhetherNormalUser("管理员请以普通用户的身份归还书籍");
        ((NormalUser)realUser).returnBook();;

}

NormalUser类

• 归还图书需要注意,内存中的数组pairOfUidAndBookIds 也需要删除,同时有效记录数borrowedCount 也要相应改变

• 对借阅书籍⽂件和全部书籍⽂件都要进⾏更新操作

java 复制代码
public void returnBook() {
        System.out.println("目前书籍信息如下: ");
        library.disPlayBook();
        System.out.println("请输入你要归还的书籍Id: ");
        int bookId = scanner.nextInt();

        //1.检查如果没有书籍,就不能归还
        if(library.getBookCount() == 0){
            System.out.println("书架没有书籍不能归还");
            return;
        }

        //加载已借阅书籍信息
        loadBorrowedBook();

        //判断要归还的书 是否存在
        Book book = library.searchById(bookId);
        if(book == null){
            System.out.println("没有归还的书籍: " +bookId);
            return;
        }
        for (int i = 0; i < borrowedCount; i++) {
            //如果2本书的ID,则认为是同⼀本书
            if (pairOfUidAndBookIds[i].getBookId() == book.getBookId()) {
                //借阅⽤户也⼀样
                if (getUserID() == pairOfUidAndBookIds[i].getUserId()) {
                    library.returnBook(bookId);
                    System.out.println(" 图书 '" + book.getTitle() + "' 已成功归还 ");
                    // ⽤最后⼀本替换归还的书
                    pairOfUidAndBookIds[i] = pairOfUidAndBookIds[borrowedCount - 1];
                    //清空最后⼀个
                    pairOfUidAndBookIds[borrowedCount - 1] = null;
                    borrowedCount--;
                    storeBorrowedBook();
                }else{
                    System.out.println(" 该书籍不是你借阅的书籍,不能归还: "+book.getTitle());
                }
                return;
            }
        }
        System.out.println(" 你没有借阅该书籍,不需要归还,书籍 ID 为: "+bookId);


}

Library类

添加returnBook方法

java 复制代码
public void returnBook(int bookId) {
        loadAllBook();
        for (int i = 0; i < getBookCount(); i++) {
            Book book = books[i];
            if(book.getBookId()==bookId) {
                book.setBorrowed(false);
                book.decreaseBorrowCount();
            }
        }
        storeBook();
}

查看个⼈借阅情况

ProxyUser类

java 复制代码
public void viewBorrowHistory(){
        checkRealUserWhetherNormalUser("管理员请以普通用户的身份查看个人借阅情况");
        ((NormalUser)realUser).viewBorrowHistory();

}

NormalUser类:

• 读取数据到内存数组

• 遍历内存数组即可

java 复制代码
public void viewBorrowHistory() {
            //读取当前借阅所有用户的借阅书籍的情况
            loadBorrowedBook();
            System.out.println("您的借阅情况如下: ");
            if (borrowedCount == 0) {
                System.out.println("目前没有借阅记录.....");
            } else {
                boolean flg = false;
                for (int i = 0; i < borrowedCount; i++) {
                    //这里只能查看属于自己借阅的情况
                    //用户ID相同的情况下,使用书籍ID查询书籍
                    if(pairOfUidAndBookIds[i].getUserId() == userID) {
                        flg = true;
                        Book book = library.searchById(pairOfUidAndBookIds[i].getBookId());
                        System.out.println(book);
                    }
                }
                if(!flg) {
                    System.out.println("你没有借阅过书籍! ");
                }
            }
}

Library类

不进行实现

查找图书

java 复制代码
public void searchBook(){
        System.out.println("请输入你要查找的图书的名称: ");
        String title = scanner.nextLine();

        Book book = searchByTitle(title);
        if(book == null) {
            System.out.println("没有你要找的这本书,你查找的书名为: "+title);
            return;
        } else {
            System.out.println("找到了你要查找的书,书的详细信息如下: ");
            System.out.println(book);
        }
}

在Library类中添加

java 复制代码
private Book searchByTitle(String title) {
        loadAllBook();
        for (int i = 0; i < getBookCount(); i++) {
            Book book = books[i];
            if(book.getTitle().equals(title)) {
                return book;
            }
        }
        return null;
}

为了更好的观看,我们将Book类的toString方法修改一下

java 复制代码
public String toString() {
        return "Book{" +
                "bookId=" + bookId +
                ", 书名: '" + title + '\'' +
                ", 作者: '" + author + '\'' +
                ", 类别: '" + category + '\'' +
                ", 出版年份: " + publishYear +
                ", 是否被借出: " + ((isBorrowed) ? "已借出" : "未借出") +
                ", 借阅次数: " + borrowCount +
                ", 上架日期: " + shelfDate +
                '}';
}

这是学完JavaSE的第一个项目,到此整个项目便完成了,文章篇幅较长,欢迎各位读者在评论区指出本文代码的其他问题.

相关推荐
zhengfei6112 小时前
精选的优秀法证分析工具和资源列表
开发语言·php
indexsunny2 小时前
互联网大厂Java求职面试实战:Spring Boot微服务与Kafka消息队列应用解析
java·数据库·spring boot·微服务·面试·kafka·jpa
u0109272713 小时前
实时数据流处理
开发语言·c++·算法
shuair3 小时前
springboot整合redisson单机模式
java·spring boot·后端
PacosonSWJTU3 小时前
mac-python解释器理解与python安装
开发语言·python
Remember_9933 小时前
Java 单例模式深度解析:设计原理、实现范式与企业级应用场景
java·开发语言·javascript·单例模式·ecmascript
urkay-3 小时前
Android 中实现 HMAC-SHA256
android·开发语言·python
代码or搬砖3 小时前
ReentranLock中AQS讲解
java·开发语言·redis
rainbow68893 小时前
C++智能指针实战:从入门到精通
java·开发语言