用 Java 模拟一个图书馆。包括创建图书、创建读者、借书、还书、列出所有图书、列出所有读者、列出已借出的图书、列出过期未还的图书等功能。每个读者最多只能借 3 本书,每个书最多只能借 3 个星期,超过就算过期。
下面是一个命令行下的实现。这个例子的主要目的是向初学者展示内部类的好处。Command 及其子类都是 LibrarySimulator 的内部类。它们可以无阻碍的访问 LibrarySimulator 的成员。使用内部类,而不是大量的 if-else,让程序更容易扩展。
- 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;
- }