面向对象思想实现图书管理系统

ps:

此工程分为四个包,分别是input(用于输入输出和给出提示信息),command(定义行为),user(普通用户和管理员用户)和main(主函数)

package 图书管理系统;

import 图书管理系统.book.BookStorage;
import 图书管理系统.command.IExecutable;
import 图书管理系统.input.Input;
import 图书管理系统.input.QuitException;
import 图书管理系统.user.User;
import 图书管理系统.user.UserStorage;

public class Main {
    // 由于输入过程,需要处理一些杂事
    // 1) 打印提示信息
    // 2) 读取用户输入
    // 3) 用户有没有按 Ctrl + D 退出
    // 直接封装一个对象去处理输入的问题
    public static void main(String[] args) {
        // 0. 实例化一个负责处理输入的对象
        Input input = new Input();

        // 1. 我们的书架对象,在应用运行过程中,只需要一份
        // 2. 改从文件中加载数据
        BookStorage bookStorage = BookStorage.loadFromFile();

        try {
            // 1. 用户登录
            // 我们需要一个用户管理的对象,由该对象完成用户登录的具体操作
            // 1.1. 要求用户输入
            // 1.2. 判断用户角色 ...
            UserStorage userStorage = new UserStorage();
            User user = userStorage.login(input);

            // 2. 进入一个循环中
            while (true) {
                // 2.1 打印用户角色对应的菜单,并且让用户选择
                // execute : 执行
                // executable : 具备可以执行的能力
                // IExecutable : 计划用接口去表示
                // command: 命令
                IExecutable command = input.menuAndSelect(user);
                // 2.2 根据用户的选择,执行对应的操作命令
                command.execute(bookStorage, user, input);
            }
        } catch (QuitException exc) {
            // 用户要退出,我们什么都不做
        }

        // 3. 用户退出,打印退出信息
        System.out.println("欢迎下次使用");
    }
}

book包:

package 图书管理系统.book;

import 图书管理系统.user.User;

public class Book implements Comparable {
    public String name;
    public String author;
    public String type;
    public int price;
    public String borrowedBy;

    @Override
    public int compareTo(Object o) {
        Book b=(Book) o;
        return name.compareTo(b.name);// 自然顺序是以书名为顺序
        // 书名也是 String 类型,也得遵守对象的比较规则
    }

    @Override
    public String toString() {
        //强行用StringBuilder
        String.format("《%s》by %s,[%s],%d",name,author,type,price);
        StringBuilder sb=new StringBuilder();
        if (borrowedBy==null){
            sb.append("可借用!");
        }else {
            sb.append("被");
            sb.append(borrowedBy);
            sb.append("借走了");
        }
        return sb.toString();
    }

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

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

    public void borrowed(User user){
        borrowedBy=user.getUsername();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Book book = (Book) o;
        return name.equals(book.name);
    }

    public boolean isBorrowedBy(User user){
        if (borrowedBy==null){
            return false;
        }
        return borrowedBy.equals(user.getUsername());
    }

    public void returned(User user){//换书操作
        borrowedBy=null;
    }
}
package 图书管理系统.book;

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

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

    public BookStorage(){
        array=new Book[20];
        size=0;
    }

    // 要让静态方法能使用,也得使用静态属性
    private static final File file=new File("C:\\java data\\.metadata\\book-storage.txt");
    //直接使用类名调用的一定是静态方法
    public static BookStorage loadFromFile(){
        BookStorage bookStorage=new BookStorage();
        if (!file.exists()){
            return bookStorage;    //说明第一次运行,创建一个空对象
        }
        //开始加载
        try {
            Scanner sc=new Scanner(file,"UTF-8");
            while (sc.hasNextLine()){
                String line=sc.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 borrowedByStr=group[4];
                String borrowedBy;
                if (borrowedByStr.equals("null")){
                    borrowedBy=null;// 把字符串 "null" 转成 null
                }else {
                    borrowedBy=borrowedByStr;
                }
                Book book=new Book();
                book.name=name;
                book.author = author;
                book.type = type;
                book.price = price;
                book.borrowedBy = borrowedBy;

                bookStorage.add(book);
            }
            sc.close();
            return bookStorage;
        }catch (IOException e){
            throw new RuntimeException(e);
        }

    }

    public void add(Book book) {
        //尾插
        ensureCapacity();
        array[size++]=book;
        saveToFile();
    }

    public Book[] toArray(){
        //return array;   // 直接返回可不可以?   语法上成立。不可以
        // 理论有 两条:
        // 1. 返回 array,它的长度是 array.length ,但我们真正的书籍只有 size 本
        // 应该是返回一个 长度是 size 的数组,而不是长度是 array.length 的数组
        // 2. 一旦把 array 返回出去之后(数组对象逃逸了)
        // 别人会怎么操作数组对象,我们完全无法控制
        // 所以可能打破顺序表的一致性问题。比如 Arrays.fill(array, null)
        // size > 0 但是 array 中全部是 null
        return Arrays.copyOf(array,size);
        // 实际上还是有风险的,因为只是浅拷贝
        // 如果使用者有意还是无意改了 Book 对象属性
        // 我们还是会收到影响的
        // 最好的办法是做深拷贝,但这里不做那么复杂了
    }


    private void ensureCapacity() {
        if (size<array.length){
            return;
        }
        array= Arrays.copyOf(array,array.length*2);
    }

    // TODO: 我们这里隐含一个假设,就是我们的书架中,不允许出现同名的书籍
    // TODO: 否则删除的时候操作有歧义
    // TODO: 这个判断,我们在 AddBookCommand 中是没有操作
    public Book searchByName(String name){
        for (int i=0;i<size;i++){
            Book book=array[i];
            if (book.equals(name)){
                return book;
            }
        }
        return null;
    }

    // 原则上,如果顺序表要保持原有顺序的情况下,删除某个元素(可能在中间)
    // 则时间复杂度必然是 O(n) 的
    // 我们这里需要保持顺序么?array 中的原始顺序其实在当前需求下不是那么重要!
    public void remove(Book name){
        for (int i=0;i<size;i++){
            Book item=new Book();
            if (item.equals(name)){
                array[i]=array[size-1];
                array[size-1]=null;
                size--;
                return;
            }
        }
        saveToFile();
    }

    // 普通方法,保存是当前 BookStorage 对象的内容
    public void saveToFile() {
        try{
            PrintWriter writer=new PrintWriter(file,"UTF-8");
            for (int i=0;i<size;i++){
                Book book=array[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);
        }
    }



}

input包:

package 图书管理系统.input;

import 图书管理系统.command.IExecutable;
import 图书管理系统.user.User;

import java.util.Scanner;

public class Input {
    Scanner scanner=new Scanner(System.in);
    public String prompt(String prompt){
        System.out.println(prompt+":");
        System.out.print(">");
        if (!scanner.hasNextLine()){//说明用户按下了ctrl+D,想要退出
            throw new QuitException();//通过异常向外部通知
        }
        return scanner.nextLine();
    }

    public IExecutable menuAndSelect(User user){
        IExecutable[] supportedCommands = user.getSupportedCommands();
        while (true){
            showMenu(supportedCommands);
            // 角色不同,理论上拥有的功能不同
            // 需要当前用户信息
            String selectStr=prompt("请选择您要进行的操作");
            try {
                int select=Integer.parseInt(selectStr);
                if (select>=1&select<=supportedCommands.length){
                    return supportedCommands[select-1];
                }
                System.out.println("请输入正确的序号!");
            }catch (NumberFormatException exc){
                System.out.println("请输入正确的数字");
            }
        }
    }

    private void showMenu(IExecutable[] supportedCommands) {
        // 2. 遍历并打印每个命令的名称,显示操作菜单
        System.out.println("----------------");
        for (int i=0;i<supportedCommands.length;i++){
            IExecutable command=supportedCommands[i];
            System.out.printf("    %2d. %s\n", i + 1, command.getName());
        }
        System.out.println("----------------");
    }

}
package 图书管理系统.input;

public class QuitException extends RuntimeException{
}

user包:

package 图书管理系统.user;

import 图书管理系统.command.IExecutable;

public abstract class User {
    private String username;

    public User(String name){
        this.username=name;
    }
    public String getUsername(){
        return username;
    }
    // 所以也完全不知道支持哪些命令
    // 应该定义成抽象方法,供子类去实现
    public abstract IExecutable[] getSupportedCommands();
}

package 图书管理系统.user;

import 图书管理系统.command.*;

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

    @Override
    public IExecutable[] getSupportedCommands() {
        return new IExecutable[] {
                // 管理员支持 添加书籍命令
                new AddBookCommand(),
                // 管理员支持 删除书籍命令
                new RemoveBookCommand(),
                // 管理员支持 根据书名列出书籍命令
                new ListBookOrderByNameCommand(),
                // 管理员支持 根据价格列出书籍命令
                new ListBookOrderByPriceCommand(),
                // 管理员支持 根据借阅情况列出书籍命令
                new ListBookOrderByBorrowedCommand()
        };
    }
}
package 图书管理系统.user;

import 图书管理系统.command.*;

public class CommandUser extends User {
    public CommandUser(String name) {
        super(name);
    }

    @Override
    public IExecutable[] getSupportedCommands() {
        return new IExecutable[] {
                new ListBookOrderByNameCommand(),
                new ListBookOrderByPriceCommand(),
                new ListBookOrderByBorrowedCommand(),
                new BorrowBookCommand(),
                new ReturnBookCommand()
        };
    }
}
package 图书管理系统.user;

import 图书管理系统.input.Input;

public class UserStorage {
    // 提前定义一些用户名,作为管理员
    private static final String[] ADMIN_USERNAMES={
      "文佳怡",
      "耿超扬"
    };

    private boolean isAdmin(String name){
        for (String admin:ADMIN_USERNAMES){
            if (admin.equals(name)){
                return true;
            }
        }
        return false;
    }

    public User login(Input input){
        // 1. 先让用户输入用户名
        //prompt:提示符
        String username=input.prompt("请输入用户名");
        // 2. 根据用户名,角色是管理员还是普通的用户
        // 3. 根据不同的角色,创建不同的用户
        // admin: 管理员
        if (isAdmin(username)){
            return new AdminUser(username);
        }else {
            return new CommandUser(username);
        }
    }
}

comand:

package 图书管理系统.command;

import 图书管理系统.book.Book;
import 图书管理系统.book.BookStorage;
import 图书管理系统.input.Input;
import 图书管理系统.user.User;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Comparator;

// 由于 AbsListBookCommand 是 抽象类,允许出现抽象方法
public abstract class AbsListBookCommand implements IExecutable {
    protected abstract Comparator getComparator();

    @Override
    public void execute(BookStorage bookStorage, User user, Input input) {
        Comparator comparator=getComparator();
        Book[] books=bookStorage.toArray();
        if (comparator==null){
            Arrays.sort(books);
        }else {
            Arrays.sort(books,comparator);
        }
        for (Book book:books){
            System.out.println(book);
        }
    }
}
package 图书管理系统.command;

import 图书管理系统.book.Book;
import 图书管理系统.book.BookStorage;
import 图书管理系统.input.Input;
import 图书管理系统.user.User;

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

    @Override
    public void execute(BookStorage bookStorage, User user, Input input) {
        System.out.println("开始添加书籍,首先读取书籍信息:");
        String name = input.prompt("请输入书籍名称");
        String author = input.prompt("请输入书籍作者");
        String type = input.prompt("请输入书籍类型");
        int price;
        while (true) {
            try {
                String priceStr = input.prompt("请输入书籍价格(必须是数字)");
                price = Integer.parseInt(priceStr);
                break;
            } catch (NumberFormatException exc) {
                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);
    }
}

package 图书管理系统.command;

import 图书管理系统.book.Book;
import 图书管理系统.book.BookStorage;
import 图书管理系统.input.Input;
import 图书管理系统.user.User;

public class BorrowBookCommand implements IExecutable {
    @Override
    public void execute(BookStorage bookStorage, User user, Input input) {
        // 通过书名,完成借阅
        System.out.println("开始借阅书籍,首先读取书籍信息:");
        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;
        }

        // 由于 book 返回的引用,指向的书籍对象,和 BookStorage 中保存的 Book 对象是同一个对象
        // 通过 book 引用修改,也会让 BookStorage 中生效
        book.borrowed(user);
        bookStorage.saveToFile();

        System.out.println(user.getUsername() + " 完成了 借阅书籍的操作: " + name);
    }

    @Override
    public String getName() {
        return "借阅书籍";
    }
}

package 图书管理系统.command;

import 图书管理系统.book.BookStorage;
import 图书管理系统.input.Input;
import 图书管理系统.user.User;

public interface IExecutable {
    void execute(BookStorage bookStorage, User user, Input input);
    String getName();
}
package 图书管理系统.command;

import 图书管理系统.book.Book;

import java.util.Comparator;

public class ListBookOrderByBorrowedCommand extends AbsListBookCommand {
    private final Comparator comparator = new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            Book b1 = (Book) o1;
            Book b2 = (Book) o2;

            int br1 = b1.borrowedBy == null ? 0 : 1;
            int br2 = b2.borrowedBy == null ? 0 : 1;

            return br1 - br2;       // 未借走的书籍比较“小”
        }
    };

    @Override
    public String getName() {
        return "根据借阅情况排序,列出书籍";
    }

    @Override
    protected Comparator getComparator() {
        return comparator;
    }
}
package 图书管理系统.command;

import java.util.Comparator;

public class ListBookOrderByNameCommand extends AbsListBookCommand {
    @Override
    public String getName() {
        return "根据书名排序,列出书籍";
    }

    @Override
    protected Comparator getComparator() {
        return null;
    }
}

package 图书管理系统.command;

import 图书管理系统.book.Book;

import java.util.Comparator;

public class ListBookOrderByPriceCommand extends AbsListBookCommand {
    private static class PriceComparator implements Comparator {

        @Override
        public int compare(Object o1, Object o2) {
            Book b1 = (Book) o1;
            Book b2 = (Book) o2;

            return b1.price - b2.price;
        }
    }

    private final Comparator comparator = new PriceComparator();

    @Override
    protected Comparator getComparator() {
        return comparator;
    }

    @Override
    public String getName() {
        return "根据价格排序,列出书籍";
    }
}
package 图书管理系统.command;

import 图书管理系统.book.Book;
import 图书管理系统.book.BookStorage;
import 图书管理系统.input.Input;
import 图书管理系统.user.User;

public class RemoveBookCommand implements IExecutable {
    @Override
    public String getName() {
        return "删除书籍";
    }

    @Override
    public void execute(BookStorage bookStorage, User user, Input input) {
        // 只支持按照名称删除
        System.out.println("开始删除书籍,首先读取要删除的书籍信息:");
        String name = input.prompt("请输入书籍名称");
        // 增加一些业务逻辑:如果书籍已经借走,则不允许删除
        Book book = bookStorage.searchByName(name);
        if (book == null) {
            // 没有找到
            System.out.println("没有这本书 " + name + ",无法删除");
            return;
        }
        // 看起来 book.isBorrowed() 和 book.borrowedBy != null 是逻辑等价的
        // 应该用 book.isBorrowed() 比 后边更好,为什么?
        // 从职责划分的角度(封装)
        // 提到了方法的重用挺好,但这里的原因不是这个
        if (book.isBorrowed()) {
            System.out.println("书籍已经被 " + book.borrowedBy + " 借走,暂时不允许删除");
            return;
        }

        bookStorage.remove(book);

        System.out.println(user.getUsername() + " 完成了 删除书籍的操作: " + name);
    }
}
package 图书管理系统.command;

import 图书管理系统.book.Book;
import 图书管理系统.book.BookStorage;
import 图书管理系统.input.Input;
import 图书管理系统.user.User;

public class ReturnBookCommand implements IExecutable {
    @Override
    public void execute(BookStorage bookStorage, User user, Input input) {
        // 通过书名,完成归还
        System.out.println("开始借阅书籍,首先读取书籍信息:");
        String name = input.prompt("请输入书籍名称");

        Book book = bookStorage.searchByName(name);
        if (book == null) {
            // 没有找到
            System.out.println("没有这本书 " + name + ",无法归还");
            return;
        }

        if (!book.isBorrowed()) {
            System.out.println("书籍没有被借走,不需要归还");
            return;
        }

        if (!book.isBorrowedBy(user)) {
            System.out.println("书籍不是被 " + user.getUsername() + " 借走的,无法归还");
            return;
        }

        book.returned(user);
        bookStorage.saveToFile();

        System.out.println(user.getUsername() + " 完成了 归还书籍的操作: " + name);
    }

    @Override
    public String getName() {
        return "归还书籍";
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值