图书管理系统(java篇)

     经过了系统的学习了javase的基础,这个练习对我来说是真的深刻的体会到了对java来说“万物皆对象”这句话。这也是第一次认识到java这门语言在书写上面向对象与c语言的面向过程的不同之处。

对于图书管理系统来说,从整体上讲可以划分为四个部分。<<user, input, command, book>>

Main方法:

1.User对象(根据用户名不同,角色不同【管理员,普通用户】,以多态的形式来加以体现)。

2.进入命令循环。

(1) 打印操作菜单,供用户选择。

(2) 操作台读取用户的选择内容,以Executable 对象来体现。(以多态形式加以体现)

(3)执行命令(管理员与普通用户执行的方法不同,所有方法最终的执行,是由对象类型决定的)

import book.Book;
import book.BookStorage;
import command.IExecutable;
import input.Input;
import input.QuitException;
import user.User;
import user.UserStorage;

public class Main {

    public static void main(String[] args) {
        //  实例化一个负责处理输入的对象
        Input input = new Input();
        //  我们的书架对象,在应用运行过程中,只需要一份
        BookStorage bookStorage = new BookStorage();
        try {

            UserStorage userStorage1= new UserStorage();
            User user = userStorage1.login(input);
            while (true) {
               /*  打印用户角色对应的菜单,并且让用户选择
                 execute : 执行
                 executable : 具备可以执行的能力
                 IExecutable : 计划用接口去表示*/
                IExecutable command = input.actionMenu(user);
                // 根据用户的选择,执行对应的操作命令
                command.execute(bookStorage, user, input);
            }
        } catch (QuitException exc) {
        }
        System.out.println("欢迎下次使用");
    }
}

一.input 类

(1)负责用户在操作台上进行登录操作。

(2)打印操作菜单,供用户选择。(用户不同,操作菜单不同)

input类 完整代码:

package input;

import command.IExecutable;
import user.User;

import java.util.Scanner;

public class input {
    //用户登录
    private  final Scanner scanner = new Scanner(System.in);
    public String Prompt(String prompt){
        System.out.println(prompt+":");
        if(!scanner.hasNextLine()){
            // 用户按下 Ctrl + D 了
            // 说明用户想退出了
            // 通过异常的方式,向外通知
            throw new RuntimeException();
        }
        return scanner.nextLine();
    }

    //  打印操作菜单,供用户选择(用户不同,操作菜单不同)
    public IExecutable  actionMenu(User user){
        IExecutable[] supportedCommands = user.getSupportedCommands();
        //supportedCommands  是对象类型的数组。

        while(true){  //获取不同用户 所支持的不同功能
          showMenu(supportedCommands);  //打印菜单
          String str= Prompt("请选择您要进行的操作:");   //将用户输入的内容赋值给str变量
            int select = Integer.parseInt(str);  //将字符串参数作为有符号的十进制整数进行解析
            //比较用户输入字符选择命令是否正确
            if(select>=1&&select<=supportedCommands.length)
            {
                return supportedCommands[select - 1];
            }


        }

    }

public void showMenu( IExecutable[] supportedCommands ){
        //依次打印用户所支持的命令,打印每个命令的名称,显示操作菜单
    for(int i = 0;i<supportedCommands.length;i++){
         IExecutable supportedCommand = supportedCommands[i];
        System.out.printf("%2d","%s",i+1, command.getName());
    }
}
}

二.user对象

(1)对于用户来说分为普通用户与管理员。

先创建一个抽象类User,让CommonUser与AdminUser(管理员)继承User的子类均重写User内的方法以实现区分普通用户与管理员的目的。

抽象类 User:

package user;

import command.IExecutable;

public abstract class User {
    private String username;

    public User(String username) {   //构造方法
        this.username = username;
    }

    public String getUsername() {   //get 方法
        return username;
    }

/*
    作为抽象的类,用户,不知道自己的角色
     所以也完全不知道支持哪些命令
     应该定义成抽象方法,供子类去实现*/
    public abstract IExecutable[] getSupportedCommands();
}

CommandUser类:

package user;

import command.IExecutable;

public class CommonUser extends User {


    public CommonUser(String username) {
        super(username);
    }

    @Override
    public IExecutable[] getSupportedCommands() {
        return new IExecutable[]{
                //支持普通用户实现的功能
     
        };
    }
}

(2)对于一个用户如何区分为普通用户还是管理员,因此需要定义一个类去实现这个功能。我们使用UserStorage 类来实现:

package user;

import input.Input;

public class UserStorage {

 private final String[] Admin_user = {"lihuanting"};
 public boolean isAdmin(String user){
     for (String admin: Admin_user) {
      if(user.equals(admin)) { // 判断是否相等,使用 equals
          return true;
      }
     }
     return false;
 }

 public User login(Input input){   //传入一个Input对象
     String username = input.Prompt("请输入用户名");
     if(isAdmin(username)){
         return new AdminUser(username);
     }
     return  new CommonUser(username);
 }



}

三.从book角度去分析:

1)每本书(包括每本书的信息等),使用Book类 去实现它。

package book;

public class Book {
    public String name;
    public String author;
    public String type;
    public int price;
    public String borrowedBy;       // 如果没人借走,就是 null

    public boolean isBorrowed() {
        return borrowedBy != null;
    }

    public boolean equalsByName(String name) {
        // 1. 也是为了封装,看起是一行一个方法
        // 2. name 是 String 类型,判断相等性,也需要使用 equals 判断
        return this.name.equals(name);
    }


}

2)放书的书架(包括其放书,取书功能),使用BookStorage类来实现它。

package book;

import java.util.Arrays;

public class BookStorage {
    private Book[] library;
    private int size;

    public BookStorage(Book[] library, int size) {
        size = 0;
        library = new Book[20];
    }
    // 尾插
    public void add(Book book) {
        // 1. 确认容量够用
        ensureCapacity();

        library[size++] = book;
    }

    private void ensureCapacity() {
        if (size < library.length) {
            // 说明至少还有一个空间,容量够用
            return;
        }

        // 否则进行扩容
        library = Arrays.copyOf(library, library.length * 2);
    }

    public Book[] toArray() {
        return Arrays.copyOf(library, size);  // 实际上还是有风险的,因为只是浅拷贝
    }

    //  我们这里隐含一个假设,就是我们的书架中,不允许出现同名的书籍
    public Book searchByName(String name) {
        // 从 DS 角度,就是顺序表的元素查找问题
        for (int i = 0; i < size; i++) {
            Book book = library[i];
            if (book.equalsByName(name)) {
                return book;
            }
        }
        return null;
    }

    // 原则上,如果顺序表要保持原有顺序的情况下,删除某个元素(可能在中间)
    // 则时间复杂度必然是 O(n) 的
    // 我们这里需要保持顺序么?array 中的原始顺序其实在当前需求下不是那么重要!
    public void remove(Book book) {
        for (int i = 0; i < size; i++) {
            Book item = library[i];
            // Book 正确的重写了 equals 方法
            if (item.equals(book)) {
                // 把最后一个元素覆盖到此处
                library[i] = library[size - 1];
                // 把最后一个元素置为 null
                library[size - 1] = null;
                // 减少 size
                size--;
                return;
            }
        }
    }
}



四.command(命令)

(1)可以先实现一个接口类,IExecutable。

package command;

import book.BookStorage;
import input.Input;
import user.User;

public interface IExecutable {
    void execute(BookStorage bookStorage, User user, Input input);
    String getName();
}

(2)对于命令来说大家可以根据需要来添加,在此我举几个例子来向大家来说明以下。

1.向书架中添加书籍,使用AddBookCommand类来实现。

package command;

import book.Book;
import book.BookStorage;
import input.Input;
import user.User;

public class AddBookCommand implements IExecutable {

    @Override
    public void execute(BookStorage bookStorage, User user, Input input) {
        System.out.println("开始添加书籍,开始读取书籍信息:");
        final String name = input.Prompt("请输入书籍名称:");
        final String author = input.Prompt("请输入书籍作者:");
        final String type = input.Prompt("请输入书籍类型:");
        int price;
        while (true) {
            try {
                final String priceStr = input.Prompt("请输入书籍价格(必须是数字)");
                price = Integer.parseInt("priceStr");
                break;
            } catch (NumberFormatException e) {
                System.out.println("价格格式错误");
            }
        }
        // 刚加架的书肯定没被借阅
        Book book = new Book();
        book.name = name;
        book.author = author;
        book.type = type;
        book.price = price;
        book.borrowedBy = null;
        System.out.println("将书籍添加到 书架上");
        bookStorage.add(book);
        System.out.println(user.getUsername()+"完成了添加书籍的操作:"+name);
    }

    @Override
    public String getName() {
        return "添加书籍";
    }
}

2.从书架中删除书籍,使用RemoveBookCommand类来实现。

package command;

import book.Book;
import book.BookStorage;
import input.Input;
import user.User;

public class RemoveBookCommand implements IExecutable {

    @Override
    public void execute(BookStorage bookStorage, User user, Input input) {
        System.out.println("开始进行删除书籍的操作:");
        final String name = input.Prompt("请输入要删除的书籍是:");
        //增加一些业务逻辑,如果书籍已经借走,则不允许删除
        Book book = bookStorage.searchByName(name);
        if (book == null) {
            System.out.println("没有这本书" + name + "无法删除");
            return;
        }
        if (book.isBorrowed()) {
            System.out.println("书籍已经被" + book.borrowedBy + "借走,暂不允许删除");
            return;
        }
        bookStorage.remove(book);
        System.out.println(user.getUsername() + "完成了删除书籍的操作" + name);
    }
    @Override
    public String getName() {
        return "删除书籍";
    }
}

提示:(1)书籍信息需要持久化存储,因此需要想办法把数据放到硬盘上,因此需要将书籍信息储存到文件中,使用只需要操作文件来对书籍信息进行提取。大家可以根据自己的需求来修改文件地址。

(2)我们需要一种序列化格式以便于在文件中读取到书籍信息。例如:每一行存储一本书。书与属性之间用@分割。

修改后的BookStorage类:

package book;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Scanner;

public class BookStorage {
    private Book[] library;
    private int size;

    public BookStorage() {
        size = 0;
        library = new Book[20];
    }
    //要让静态方法能使用,也必须要定义静态属性
    private static File file = new File("D:\\Desk\\综合练习\\图书管理系统\\src\\file\\bookStorage.txt");
    public static  BookStorage loadFromFile(){
        BookStorage bookStorage = new BookStorage();
        if (!file.exists()) {
            // 文件不存在,说明是第一次运行,就创建空对象
            return bookStorage;
        }

        // 开始进行加载
        try {
            Scanner scanner = new Scanner(file, "UTF-8");
            while (scanner.hasNextLine()) {
                String line = scanner.nextLine();

                // split 按照指定字符串,进行切割
                String[] group = line.split("@");
                if (group.length != 5) {
                    throw new RuntimeException("文件格式不对了");
                }

                String name = group[0];
                String author = group[1];
                String type = group[2];
                String priceStr = group[3];
                int price;
                try {
                    price = Integer.parseInt(priceStr);
                } catch (NumberFormatException exc) {
                    throw new RuntimeException("价格不是数字");
                }
                String borrowedStr = group[4];
                String borrowedBy;
                if (borrowedStr.equals("null")) {
                    borrowedBy = null;  // 把字符串 "null" 转成 null
                } else {
                    borrowedBy = borrowedStr;
                }

                Book book = new Book();
                book.name = name;
                book.author = author;
                book.type = type;
                book.price = price;
                book.borrowedBy = borrowedBy;

                bookStorage.add(book);
            }

            scanner.close();

            return bookStorage;
        } catch (IOException exc) {
            // 把受查异常转成非受查异常,这样,不会语法抱怨了
            throw new RuntimeException(exc);
        }
    }
    // 尾插
    public void add(Book book) {
        // 1. 确认容量够用
        ensureCapacity();

        library[size++] = book;
        saveToFile();
    }

    private void ensureCapacity() {
        if (size < library.length) {
            // 说明至少还有一个空间,容量够用
            return;
        }

        // 否则进行扩容
        library = Arrays.copyOf(library, library.length * 2);
    }

    public Book[] toArray() {
        return Arrays.copyOf(library, size);  // 实际上还是有风险的,因为只是浅拷贝
    }

    //  我们这里隐含一个假设,就是我们的书架中,不允许出现同名的书籍
    public Book searchByName(String name) {
        // 从 DS 角度,就是顺序表的元素查找问题
        for (int i = 0; i < size; i++) {
            Book book = library[i];
            if (book.equalsByName(name)) {
                return book;
            }
        }
        return null;
    }

    // array 中的原始顺序其实在当前需求下不是那么重要!
    public void remove(Book book) {
        for (int i = 0; i < size; i++) {
            Book item = library[i];
            // Book 正确的重写了 equals 方法
            if (item.equals(book)) {
                // 把最后一个元素覆盖到此处
                library[i] = library[size - 1];
                // 把最后一个元素置为 null
                library[size - 1] = null;
                // 减少 size
                size--;
                return;
            }
        }
        saveToFile();
    }
    // 普通方法,保存是当前 BookStorage 对象的内容
    public void saveToFile() {
        try {
            PrintWriter writer = new PrintWriter(file, "UTF-8");
            for (int i = 0; i < size; i++) {
                Book book = library[i];

                StringBuilder sb = new StringBuilder();
                sb.append(book.name);
                sb.append("@");
                sb.append(book.author);
                sb.append("@");
                sb.append(book.type);
                sb.append("@");
                sb.append(book.price);
                sb.append("@");
                sb.append(book.borrowedBy);

                writer.println(sb.toString());
            }

            writer.flush();
            writer.close();
        } catch (IOException exc) {
            throw new RuntimeException(exc);
        }
    }

}

完整工程文件链接:https://pan.baidu.com/s/1xU1b8zE8qeK_UA7UunE9Dw?pwd=1234 
                         提取码:1234

git地址https://github.com/sunnunny22/Library.git

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值