import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.text.SimpleDateFormat; import java.util.*; /** * 一个图书馆的课程设计。主要功能: * 1. 创建图书 * 2. 创建读者 * 3. 借书 * 4. 还书 * 5. 列出所有书 * 6. 列出已借书 * 7. 列出超过日期未还的书 */ public class LibrarySimulator { // 主菜单 private static final String MAIN_MENU = "1. 列出所有的书/n" + "2. 列出已借出的书/n" + "3. 列出过期未还的书/n" + "4. 列出所有读者/n" + "5. 创建图书/n" + "6. 创建读者/n" + "7. 借书/n" + "8. 还书/n" + "9. 退出/n" + "请输入序号:"; // 选择图书类型的菜单。在借书和添加图书的时候都会用到 private static final String TYPE_MENU; // 表示一个数字的正则表达式 private static final String DIGIT_CHOICE_PATTERN = "^//d$"; // 表示非空字符串 private static final String NOT_EMPTY_PATTERN = "//S.*"; // 日期格式 static final String DATE_PATTERN = "yyyy/MM/dd"; // 验证用户输入日期的正则表达式 static final String DATE_FORMAT_PATTERN = "^//d{4}///d{2}///d{2}$"; // 预定义的图书类型 static HashMap<String, String> TYPES = new LinkedHashMap<String, String>(); static { TYPES.put("1", "科学类"); TYPES.put("2", "文学类"); // 新的类别可以继续在后面添加 TYPE_MENU = createTypeMenu(); } // 生成选择类别的菜单 private static String createTypeMenu() { String str = ""; for (String index : TYPES.keySet()) { str += index + ". " + TYPES.get(index) + "/n"; } return str + "请选择书的类型:"; } private HashMap<Integer, Command> commands = new HashMap<Integer, Command>(); private ArrayList<Book> books = new ArrayList<Book>(); private ArrayList<Reader> readers = new ArrayList<Reader>(); // 程序入口。这里创建一个 LibrarySimulator 用于模拟界面。 public static void main(String[] args) { new LibrarySimulator().start(); } /** * 构造函数 */ public LibrarySimulator() { commands.put(1, new Command1()); commands.put(2, new Command2()); commands.put(3, new Command3()); commands.put(4, new Command4()); commands.put(5, new Command5()); commands.put(6, new Command6()); commands.put(7, new Command7()); commands.put(8, new Command8()); } /** * 这里接受用户输入,执行操作,然后再等待用户输入,这样不停的循环。 */ private void start() { String index = prompt(MAIN_MENU, DIGIT_CHOICE_PATTERN); while (!index.equals("9")) { executeCommand(index); index = prompt(MAIN_MENU, DIGIT_CHOICE_PATTERN); } } // 根据序号执行命令 private void executeCommand(String index) { Command command = commands.get(Integer.parseInt(index)); if (command != null) { String result = command.execute(); System.out.println(result + "/n"); } } // 打印一条提示信息,然后读取并返回用户输入 private String prompt(String message, String pattern) { System.out.print(message); if (pattern == null) { return readInput(); } else { String result = ""; while (!result.matches(pattern)) { result = readInput(); } return result; } } // 读取用户输入 private String readInput() { try { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); return reader.readLine(); } catch (IOException e) { e.printStackTrace(); return ""; } } // 根据名字查找读者。找不到则返回 null。 private Reader getReaderByName(String readerName) { for (Reader reader : readers) { if (reader.getName().equals(readerName)) { return reader; } } return null; } // 根据名字查找图书。找不到则返回 null。 private Book getBookByName(String bookName) { for (Book book : books) { if (book.getName().equals(bookName)) { return book; } } return null; } /*===================================================================*/ /** * 代表命令的抽象类 */ private abstract class Command { protected abstract String execute(); } /////////////////////////////////////////////////// 列出所有图书 private class Command1 extends Command { protected String execute() { for (Book book : getBooks()) { System.out.println(book); // 这里会自动调用 book.toString() } return "命令完成。"; } private ArrayList<Book> getBooks() { ArrayList<Book> result = new ArrayList<Book>(); for (Book book : books) { if (isValid(book)) { result.add(book); } } return result; } // 考虑到第 1、2、3 条命令大体相同,这里提供了一个给子类覆写的方法 protected boolean isValid(Book book) { return true; } } ///////////////////////////////////////////////////// 列出已借出的书。 // 注意它的父类不是 Command,而是 Command1。这样节省了很多重复代码 private class Command2 extends Command1 { @Override protected boolean isValid(Book book) { return book.isBorrowed(); } } //////////////////////////////////////////////////////// 列出过期未还的书 private class Command3 extends Command1 { @Override protected boolean isValid(Book book) { // 判断一本书接触过期与否的方法最好在 Book 类中去实现。 return book.isExpired(); } } /////////////////////////////////////////////// 创建图书 private class Command5 extends Command { protected String execute() { String type = getType(); String name = getName(); if (getBookByName(name) == null) { books.add(new Book(type, name)); return "图书添加成功。"; } else { return "图书添加失败:名称已存在。"; } } // 获得用户输入的书名 private String getName() { return prompt("请输入书名:", NOT_EMPTY_PATTERN); } // 获得用户选择的图书类型 private String getType() { return prompt(TYPE_MENU, DIGIT_CHOICE_PATTERN); } } /////////////////////////////////////////////////////// 列出所有读者 private class Command4 extends Command { protected String execute() { for (Reader reader : readers) { System.out.println(reader); } return "命令完成。"; } } /////////////////////////////////////////////////////// 创建读者 private class Command6 extends Command { protected String execute() { String name = getName(); if (getReaderByName(name) == null) { readers.add(new Reader(name)); return "读者创建成功。"; } else { return "读者创建失败:名字已经存在。"; } } public String getName() { return prompt("请输入读者名字:", NOT_EMPTY_PATTERN); } } /////////////////////////////////////////////////////// 借书 private class Command7 extends Command { protected String execute() { Reader reader = getReader(); if (reader == null) { System.out.println("命令取消。"); return ""; } Book book = getBook(); if (book == null) { System.out.println("命令取消。"); return ""; } String borrowDate = getBorrowDate(); book.borrowBy(reader.getName(), borrowDate); reader.addBorrowCount(); return "成功借出。"; } private String getBorrowDate() { String now = new SimpleDateFormat(LibrarySimulator.DATE_PATTERN).format(new Date()); String date = null; while (date == null || !date.matches(DATE_FORMAT_PATTERN)) { date = prompt("请输入结束日期(如" + now + ")", NOT_EMPTY_PATTERN); } return date; } private Book getBook() { Book book = null; while (book == null || book.isBorrowed()) { String bookName = prompt("请输入图书名字:", null); if (bookName.equals("")) { return null; } book = getBookByName(bookName); if (book == null) { System.out.println("图书不存在。"); } else if (book.isBorrowed()) { System.out.println("图书已经被借出。"); } } return book; } private Reader getReader() { Reader reader = null; while (reader == null || !reader.canBorrow()) { String readerName = prompt("请输入读者名字:", null); if (readerName.equals("")) { return null; } reader = getReaderByName(readerName); if (reader == null) { System.out.println("读者不存在。"); } else if (!reader.canBorrow()) { System.out.println("该读者已经借了" + Reader.MAX_BORROW + " 本书,不能继续借了。"); } } return reader; } } ///////////////////////////////////////////// 还书 private class Command8 extends Command { protected String execute() { Reader reader = getReader(); if (reader == null) { System.out.println("命令取消。"); return ""; } Book book = getBook(reader); if (book == null) { System.out.println("命令取消。"); return ""; } reader.reduceBorrowCount(); book.returned(); return "操作成功。"; } private Book getBook(Reader reader) { Book book = null; while (book == null || !reader.getName().equals(book.getBorrower())) { String bookName = prompt("请输入图书名字:", null); if (bookName.equals("")) { return null; } book = getBookByName(bookName); if (book == null) { System.out.println("图书不存在。"); } else if (!reader.getName().equals(book.getBorrower())) { System.out.println("该读者没有借出这本书。"); } } return book; } private Reader getReader() { Reader reader = null; while (reader == null) { String readerName = prompt("请输入读者名字:", null); if (readerName.equals("")) { return null; } reader = getReaderByName(readerName); if (reader == null) { System.out.println("读者不存在。"); } } return reader; } } } // 图书 class Book { public static final int EXPIRE_DAYS = 21; // 可借出天数,超过就算过期 private String type; private String name; private String borrowedBy = null; private String borrowDate = null; Book(String type, String name) { this.type = type; this.name = name; } @Override public String toString() { String str = String.format("类别:%s 书名:%s", LibrarySimulator.TYPES.get(type), name); if (isBorrowed()) { str += " 借出人:" + borrowedBy + " 借出时间:" + borrowDate; } return str; } public boolean isBorrowed() { return borrowedBy != null; } public String getName() { return name; } public String getBorrowDate() { return borrowDate; } /** * 图书借出 * * @param name 读者名字 * @param date 借出日期。格式:参见 {@link LibrarySimulator#DATE_PATTERN} */ public void borrowBy(String name, String date) { this.borrowedBy = name; this.borrowDate = date; } public boolean isExpired() { if (borrowDate == null) { return false; // 没有借出的书不出现在过期未还列表当中,所以这里返回 false。 } // 从当前时间往前推 3 个星期,如果还在借书日期之后,说明借书已经超过 3 个星期了 String threeWksAgo = get3WeeksAgo(); return threeWksAgo.compareTo(borrowDate) > 0; } // 获得 3 个星期前的日期 private String get3WeeksAgo() { SimpleDateFormat f = new SimpleDateFormat(LibrarySimulator.DATE_PATTERN); Calendar c = Calendar.getInstance(); c.add(Calendar.DAY_OF_MONTH, -EXPIRE_DAYS); return f.format(c.getTime()); } public void returned() { this.borrowBy(null, null); } public String getBorrower() { return borrowedBy; } } // 读者 class Reader { // 每位读者最多可同时借出 3 本书 public static final int MAX_BORROW = 3; private String name; private int borowCount = 0; public int getBorowCount() { return borowCount; } Reader(String name) { this.name = name; } public String getName() { return name; } public void addBorrowCount() { borowCount++; } public void reduceBorrowCount() { borowCount--; } public boolean canBorrow() { return borowCount < MAX_BORROW; } @Override public String toString() { return name; } }