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