用 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; } } }