首页 > 代码库 > Java 小例子:图书馆课程设计(Java 8 版)

Java 小例子:图书馆课程设计(Java 8 版)

用 Java 模拟一个图书馆。包括创建图书、创建读者、借书、还书、列出所有图书、列出所有读者、列出已借出的图书、列出过期未还的图书等功能。每个读者最多只能借 3 本书,每个书最多只能借 3 个星期,超过就算过期。

这个例子跟 http://blog.csdn.net/yidinghe/article/details/3940437 相比,增加了 Java 8 特有的语法,包括:Lambda 表达式,java.time 日期 API,streaming API 等。功能也比前者稍微完善了些,体积有所减小。

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 图书馆例子 Java 8 版本
 * created at 2014/11/4
 *
 * @author Yiding
 */
public class LibraryManager {

    public static final Scanner SCANNER = new Scanner(System.in);

    public static final String NUMBERS_ONLY = "^\\d+$"; // 表示只允许输入数字

    public static final String ANY_CONTENT = "^\\S+$";  // 表示可以输入任何内容

    public static void main(String[] args) {
        new LibraryManager().start();
    }

    ////////////////////////////////////////////////////////////////

    private Library library = new Library();

    private ArrayList<Command> commands = new ArrayList<>();

    private String mainMenu;

    /**
     * 构造方法
     */
    public LibraryManager() {
        initCommands();
        initStudents();
        initBooks();
    }

    private void initBooks() {
        this.library.addBook("论程序员的自我修养", "带鱼", BookType.科学类.toString());
        this.library.addBook("印度四大名著全集", "阿达木", BookType.文学类.toString());
        this.library.addBook("睡眠的好处", "程序员阿迪", BookType.科学类.toString());
        this.library.addBook("架构师2014年10月刊", "美丽女人网", BookType.杂志.toString());
    }

    private void initStudents() {
        this.library.students.add(new Student("张三"));
        this.library.students.add(new Student("李四"));
        this.library.students.add(new Student("王五"));
    }

    /**
     * 初始化命令和主菜单
     */
    private void initCommands() {
        addCommand(new ListCommand<>("所有图书:", () -> library.books), "查询所有图书");
        addCommand(new ListCommand<>("所有学生:", () -> library.students), "查询所有学生");
        addCommand(new AddBookCommand(), "添加图书");
        addCommand(new DeleteBookCommand(), "删除图书");
        addCommand(new BorrowBookCommand(), "借阅图书");
        addCommand(new ReturnBookCommand(), "归还图书");
        addCommand(new ListCommand<>("所有借阅过期的图书:", library::expiredBooks), "查询借阅过期的图书");
        addCommand(new ExitCommand(), "退出");

        this.mainMenu = toMenu("请输入命令", this.commands);
    }

    private void addCommand(Command command, String title) {
        command.title = title;
        this.commands.add(command);
    }

    /**
     * 开始执行交互
     */
    private void start() {
        CommandResult result;

        // 在 while 条件中判断命令的执行结果是否表示要退出程序
        // 只有 ExitCommand 的执行结果是 CommandResult.EXIT
        do {

            try {
                String command = prompt(mainMenu, NUMBERS_ONLY); // 选择命令
                result = executeCommand(command);                // 执行命令
            } catch (CommandCancelException e) {
                result = CommandResult.FAIL;
            }

            System.out.println(result.prompt + "\n");
        } while (result != CommandResult.EXIT);
    }

    /**
     * 打印一条提示消息并返回用户的输入
     *
     * @param prompt  提示消息
     * @param pattern 指定格式,如果用户的输入不符合格式则会反复提示重新输入。为空则不检查用户输入
     *
     * @return 用户的输入
     */
    private String prompt(String prompt, String pattern) {
        String userInput;

        // 在 while 条件中判断用户输入的内容是否符合 pattern 指定的格式
        // 如果 pattern 为 null 则不做判断
        do {
            System.out.print(prompt);
            userInput = SCANNER.nextLine();

            // 用户直接回车时,表示取消命令执行
            if (userInput.equals("")) {
                throw new CommandCancelException();
            }

        } while (pattern != null && !userInput.matches(pattern));

        return userInput;
    }

    // 打印一组选项并返回用户选择的选项内容
    private String prompt(String prompt, List<?> options) {
        int index = promptIndex(prompt, options);
        return options.get(index - 1).toString();
    }

    // 打印一组选项并返回用户选择的位置
    private int promptIndex(String prompt, List<?> options) {
        String menu = toMenu(prompt, options);
        int index;

        do {
            index = Integer.parseInt(prompt(menu, NUMBERS_ONLY));
        } while (index == 0 || index > options.size());

        return index;
    }

    /**
     * 生成菜单内容
     *
     * @param prompt  提示,在列出所有选项后打印出来
     * @param options 选项
     *
     * @return 主菜单内容
     */
    private <T> String toMenu(String prompt, List<T> options) {
        final ArrayList<String> lines = new ArrayList<>();
        final AtomicInteger counter = new AtomicInteger();

        options.forEach((t) -> {
            int index = counter.incrementAndGet();
            String line = index + ": " + t.toString();
            lines.add(line);
        });

        return String.join("\n", lines) + "\n" + prompt + "(1-" + lines.size() + "):";
    }

    /**
     * 执行用户命令
     *
     * @param command 用户命令序号
     *
     * @return 执行结果
     */
    private CommandResult executeCommand(String command) {
        int index = Integer.parseInt(command);

        if (index > 0 && index <= commands.size()) {
            return commands.get(index - 1).execute();
        } else {
            return CommandResult.OK;
        }
    }

    ////////////////////////////////////////////////////////////////

    static enum CommandResult {
        OK("命令已完成。"), FAIL("命令已取消。"), EXIT("");

        public String prompt;   // 在每个命令结束时打印出来

        CommandResult(String prompt) {
            this.prompt = prompt;
        }

    }

    static enum BookType {文学类, 科学类, 杂志}

    // 表示用户取消命令的异常
    static class CommandCancelException extends RuntimeException {

    }

    static class Book {

        public static final int EXPIRE_BORROW_DAYS = 21;

        public String name;

        public String author;

        public String type;

        public String borrowedBy;

        public String borrowDate;

        Book(String name, String author, String type) {
            this.name = name;
            this.author = author;
            this.type = type;
        }

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

        @Override
        public String toString() {
            return name + ",作者:" + author + "," + type +
                    (isBorrowed() ? " -- 已被'" + borrowedBy + "'于" + borrowDate + "借出" : "");
        }

        public boolean isExpired() {
            if (!isBorrowed()) {
                return false;
            }

            // 从当前时间反推过期的借阅时间。如果实际借阅时间在过期的借阅时间之前,则表示过期了
            LocalDate maxBorrowDate = LocalDate.now().minus(EXPIRE_BORROW_DAYS, ChronoUnit.DAYS);
            String maxBorrowDateStr = DateTimeFormatter.ofPattern("yyyyMMdd").format(maxBorrowDate);
            return this.borrowDate.compareTo(maxBorrowDateStr) < 0;
        }
    }

    static class Student {

        public static final int MAX_BORROW = 3;

        public String name;

        Student(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return name;
        }
    }

    ////////////////////////////////////////////////////////////////

    class Library {

        private List<Book> books = new ArrayList<>();

        private List<Student> students = new ArrayList<>();

        /**
         * 添加书籍
         *
         * @param bookName 书名
         * @param author   作者
         * @param type     类型
         *
         * @return 执行结果
         */
        public CommandResult addBook(String bookName, String author, String type) {
            if (books.stream().anyMatch((b) -> b.name.equals(bookName))) {
                System.out.println("添加失败:书名已存在");
                return CommandResult.FAIL;
            }

            this.books.add(new Book(bookName, author, type));
            return CommandResult.OK;
        }

        public List<Book> availableBooks() {
            return this.books.stream().filter((b) -> !b.isBorrowed()).collect(Collectors.toList());
        }

        public List<Book> borrowedBooks() {
            return this.books.stream().filter(Book::isBorrowed).collect(Collectors.toList());
        }

        public List<Book> expiredBooks() {
            return this.books.stream().filter(Book::isExpired).collect(Collectors.toList());
        }

        /**
         * 删除书籍
         *
         * @param index 序号
         *
         * @return 执行结果
         */
        public CommandResult deleteBook(int index) {
            this.books.remove(index);
            return CommandResult.OK;
        }

        public int countBorrowedBooks(String student) {
            return (int) this.books.stream().filter((b) -> student.equals(b.borrowedBy)).count();
        }
    }

    ////////////////////////////////////////////////////////////////

    /**
     * 表示命令的抽象类
     */
    static abstract class Command {

        public String title;  // 命令标题,将显示在主菜单中

        abstract CommandResult execute();

        @Override
        public String toString() {
            return title;
        }
    }

    // 列出满足要求的对象
    class ListCommand<T> extends Command {

        private Supplier<List<T>> supplier; // 查询满足要求的对象的方法

        private String title;               // 输出标题

        ListCommand(String title, Supplier<List<T>> supplier) {
            this.title = title;
            this.supplier = supplier;
        }

        @Override
        CommandResult execute() {
            System.out.println("\n" + title);
            supplier.get().forEach(System.out::println);
            return CommandResult.OK;
        }
    }

    // 添加图书
    class AddBookCommand extends Command {

        @Override
        CommandResult execute() {
            return library.addBook(
                    prompt("请输入书名:", ANY_CONTENT),
                    prompt("请输入作者:", ANY_CONTENT),
                    prompt("请选择书籍类型:", Arrays.asList(BookType.values()))
            );
        }
    }

    // 删除图书
    class DeleteBookCommand extends Command {

        @Override
        CommandResult execute() {
            if (library.books.isEmpty()) {
                System.out.println("没有可删除的书籍。");
                return CommandResult.FAIL;
            }

            int index = promptIndex("请选择书籍序号", library.books);
            return library.deleteBook(index - 1);
        }
    }

    // 借阅图书
    class BorrowBookCommand extends Command {

        @Override
        CommandResult execute() {
            List<Book> availableBooks = library.availableBooks();
            if (availableBooks.isEmpty()) {
                System.out.println("没有可借阅的图书。");
                return CommandResult.FAIL;
            }

            int index = promptIndex("请选择要借阅的图书", availableBooks);
            Book book = availableBooks.get(index - 1);

            String student = prompt("请选择借阅者:", library.students);
            if (library.countBorrowedBooks(student) >= Student.MAX_BORROW) {
                System.out.println("该同学不能借阅更多图书了。");
                return CommandResult.FAIL;
            }

            String bDate = prompt("请输入借阅日期(YYYYMMDD):", "^\\d{8}$");

            book.borrowedBy = student;
            book.borrowDate = bDate;
            return CommandResult.OK;
        }
    }

    // 归还图书
    class ReturnBookCommand extends Command {

        @Override
        CommandResult execute() {
            List<Book> borrowedBooks = library.borrowedBooks();
            if (borrowedBooks.isEmpty()) {
                System.out.println("没有图书需要归还。");
                return CommandResult.FAIL;
            }

            int index = promptIndex("请选择已借阅的图书", borrowedBooks);
            Book book = borrowedBooks.get(index - 1);
            book.borrowedBy = null;
            book.borrowDate = null;
            System.out.println("图书已归还。");
            return CommandResult.OK;
        }
    }

    // 退出程序
    class ExitCommand extends Command {

        @Override
        CommandResult execute() {
            return CommandResult.EXIT;
        }
    }
}


Java 小例子:图书馆课程设计(Java 8 版)