基于往期学习的类和对象、继承、多态、抽象类和接口来完成一个控制台版本的 "图书管理系统"
在控制台界面中实现用户与程序交互
任务目标:
1、系统中能够表示多本图书的信息
2、提供两种用户(普通用户,管理员)
3、普通用户:查看书籍列表,查找图书,借书,还书
4、管理员:查看书籍列表,查找图书,新增图书,删除图书
任务实现:
1、图书类
创建一个包 library 用来表示 图书管理系统 ,再创建 Book 类表示 一本的图书信息
在类里创建需要用到的属性(书名,作者,价格,类型,是否被借出)
java
package library;
public class Book {
private String name; //书名
private String author; //作者
private double price; //价格
private String type; //类型
private Boolean isBorrowed; //是否被借出
//构造方法
//当新增图书时,默认是未被借出
public Book(String name, String author, double price, String type) {
this.name = name;
this.author = author;
this.price = price;
this.type = type;
this.isBorrowed = false;
}
//提供 get/set 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Boolean getBorrowed() {
return isBorrowed;
}
public void setBorrowed(Boolean borrowed) {
isBorrowed = borrowed;
}
}
2、图书列表类
创建一个 BookList 类用来管理多个书籍对象
java
package library;
public class BookList {
//创建数组来记录多本书的信息
private Book[] books = new Book[100];
private int size = 0;//当前数组中有效元素的个数
//构造方法
public BookList(){
//默认添加几本书,方便后续测试
books[0] = new Book("西游记","吴承恩",100.0,"古典名著");
books[1] = new Book("高等数学","高斯",90.0,"自然科学");
books[2] = new Book("福尔摩斯探案集","阿瑟·柯南·道尔",110,"悬疑推理小说");
size = 3;
}
//提供 get/set 方法
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public Book getBook(int index){
return books[index];
}
public void setBooks(int index,Book book){
books[index] = book;
}
}
3、用户类
因为 普通用户 和 管理员 是从 用户 中根据不同的权限分离出来的,这里就涉及到了 继承 的知识,让 子类 继承 父类 来减少代码量
java
package library.User;
import library.IOperation;
//用户父类,派生出子类管理员和普通用户
public abstract class User {
//这里用 protected 修饰 name,可以让子类直接获取到,不用 get/set 方法
protected String name;
//当前这个类能够进行哪些操作,就往这个数组中添加对应的对象
protected IOperation[] operations;
//构造方法
public User(String name) {
this.name = name;
}
//管理员和普通用户的权限不一样,菜单面板也有所不同,
//但是在父类中没有办法确定使用哪个菜单,所以可以写成抽象类,让子类去进行重写
//通过返回用户输入的序号来决定执行不同的操作,所以使用 int 类
//写成 抽象方法 后就需要将这个父类写成 抽象类
public abstract int menu();
}
父类写到这,我们就可以去创建子类
4、普通用户类(NormalUser)
java
import java.util.Scanner;
public class NormalUser extends User{
public NormalUser(String name) {
super(name);
}
@Override
public int menu() {
//打印普通用户的菜单
System.out.println("===========================");
System.out.println("欢迎您" + name + "!");
System.out.println("1. 查看书籍列表");
System.out.println("2. 按照名字查找图书");
System.out.println("3. 借阅图书");
System.out.println("4. 归还图书");
System.out.println("0. 退出");
System.out.println("===========================");
System.out.println("请输入您的操作:");
Scanner scanner = new Scanner(System.in);
int choice = scanner.nextInt();
return choice;
}
}
5、管理员类(Admin)
java
import java.util.Scanner;
public class NormalUser extends User{
public NormalUser(String name) {
super(name);
}
@Override
public int menu() {
//打印普通用户的菜单
System.out.println("===========================");
System.out.println("欢迎您" + name + "!");
System.out.println("1. 查看书籍列表");
System.out.println("2. 按照名字查找图书");
System.out.println("3. 借阅图书");
System.out.println("4. 归还图书");
System.out.println("0. 退出");
System.out.println("===========================");
System.out.println("请输入您的操作:");
Scanner scanner = new Scanner(System.in);
int choice = scanner.nextInt();
return choice;
}
}
写到这里 书 和 用户 的代码还没有进行交互,这就是面向对象程序设计的典型特点,先创建核心的类/对象,把 核心类/对象属性、方法 搞出来,再通过主逻辑把多个 类/对象 串起来。
6、基本操作接口
创建接口是为了保证所有的类都提供 work 方法,保证 work 的参数都是一致的,也方便之后调用
(这里不使用 接口 使用 抽象类 也是可以的,主要是因为当前没有什么实例属性需要子类来继承(前文中的用户,有 name 属性需要被继承,所以所以使用了抽象类),当两种写法都可以时,优先考虑使用接口)
通过 Operation 把 用户 和 书 关联起来
java
package library.operation;
import library.BookList;
//通过这个接口来表示用户的一种基本操作
public interface IOperation {
//给出抽象方法
//此处操作的 work 方法,是针对 "多本书" 来进行操作的
//后续再创建操作相关的具体的类,实现这个接口,就要进一步实现这里的操作
void work(BookList bookList);
}
把操作单独提取成类
这些类都要实现自 IOperation 接口,并且重写其中的 work 方法
java
package library.operation;
import library.BookList;
public class AddOperation implements IOperation{
@Override
public void work(BookList bookList) {
}
}
由于当前每个操作都实现了 IOperation 接口,就只需要给这两种用户添加一个 IOperation[ ] 的数组保存能够支持的操作有什么就可以了
回到 普通用户 和 管理员 创建好数组,并在数组里写上要实现的操作
普通用户的操作
数组是 普通用户 和 管理员 都需要,所以直接在父类创建即可
java
public NormalUser(String name) {
super(name);
operations = new IOperation[]{
new ExitOperation(),
new ListOperation(),
new FindOperation(),
new BorrowOperation(),
new ReturnOperation()
};
}
注意这里创建的是数组,元素和元素直接用 ","隔开。
数组创建写在构造方法内,实例化一个用户,都要操作可以调用
管理员的操作
java
public Admin(String name) {
super(name);
operations = new IOperation[]{
new ExitOperation(),
new ListOperation(),
new FindOperation(),
new AddOperation(),
new DelOperation()
};
}
7、Main 类
当我们准备好了所有对象(书、用户、操作),就可以通过一个主逻辑将所有内容串起来
7.1 创建书籍管理对象
java
BookList bookList = new BookList();
7.2 创建用户对象
这里的身份有两个,所以需要写一个方法,让用户自己选择一个身份
java
//实例化用户对象
User user = login();
//选择身份方法
private static User login(){
//让用户输入自己的身份
Scanner scanner = new Scanner(System.in);
System.out.println("请输入您的姓名:");
String name = scanner.next();
System.out.println("请输入你的身份:(1 普通用户,2 管理员)");
int role = scanner.nextInt();
if(role == 1){
return new NormalUser(name);
} else if (role == 2) {
return new Admin(name);
}else {
System.out.println("输入有误");
return null;
}
}
7.3 构建一个主循环
用户可以不断选择要进行的操作
java
while(true){
}
7.4 显示用户对应的菜单
java
while(true){
//这里会触发多态,根据实际指向来决定是谁的菜单
int choice = user.menu();
}
7.5 根据序号,执行对应操作
java
//给user 提供一个对应的 "执行" 的方法,最终都是落到对应的 IOperation 对象上
user.work(choice,bookList);
//回到 User 类里创建对应方法
public void work(int choice, BookList bookList){
//这个操作,是通过输入的 choise 值选择数组中对应的那个操作
//这里每个操作的下标要和菜单上的序号一一对应
//这里还可以添加一个判断,判断输入的数字是否合法
if(choice < 0 && choice > operations.length){
System.out.println("输入的选项非法");
return;
}
operations[choice].work(bookList);
}
当主逻辑写到这里,就可以进行测试了
测试顺利就可以进入最后的 操作实现
8、IOperation 操作实现
8.1 ExitOperation
java
package library.operation;
import library.BookList;
public class ExitOperation implements IOperation{
@Override
public void work(BookList bookList) {
//只需要结束整个程序
System.out.println("Goodbye!");
//这里的参数是程序的退出码,不重要
System.exit(0);
}
}
8.2 ListOperation
java
package library.operation;
import library.Book;
import library.BookList;
public class ListOperation implements IOperation{
@Override
public void work(BookList bookList) {
System.out.println("查看书籍列表");
for(int i = 0 ; i< bookList.getSize();i++){
Book book = bookList.getBook(i);
System.out.println("[" + i + "]" + book);
}
}
}
通过 for 循环对我们存储书的数组进行遍历,循环的次数来自父类的 size ,循环里分别指向图书列表每一个,最后进行打印,这里在打印时添加一个下标,方便之后进行利用下标进行删除
我们会看到打印出来的结果是一段哈希码,我们需要再写一个 toString 方法,回到 Book类中
java
@Override
public String toString() {
return "[" + name + "," + author + "," + price + "," + type + "," + isBorrowed + "]";
}
这样就把我们的书籍列表打印出来了
8.3 AddOperation
让用户输入新的书籍,并添加到书籍列表中
java
package library.operation;
import jdk.swing.interop.SwingInterOpUtils;
import library.Book;
import library.BookList;
import java.util.Scanner;
public class AddOperation implements IOperation{
@Override
public void work(BookList bookList) {
System.out.println("新增图书");
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个书名:");
String name = scanner.next();
System.out.println("请输入作者:");
String author = scanner.next();
System.out.println("请输入价格:");
Double price = scanner.nextDouble();
System.out.println("请输入类型:");
String type = scanner.next();
Book book = new Book(name,author,price,type);
int size = bookList.getSize();
bookList.setBooks(size,book);
bookList.setSize(size + 1);
System.out.println("新增图书完成");
}
}
通过 书 的四个属性创建一本新的书,先获取 size 再通过 size 来找到 书 存放的位置,最后让 size+1 表示当前图书的有效位置
可以看到已经成功将新书存入书籍列表中了
8.4 DelOperation
在写删除之前先来了解一个小知识,当我们进行删除文件时,总是能非常快速的完成,无论文件大小,删除都是一瞬间。因为在硬盘里,当你进行删除时,硬盘会将你所删除的这个文件在的位置声明这里没有东西,实际上你的文件还是存在,只是之后新的数据会覆盖在这个区域上。这也是文件误删后可以通过技术手段进行找回的原因
在这写删除仍然使用这个概念,当我们删除图书时,只需要将有效图书个数-1就可以,书依然存在数组里但是不会显示出来。
这里又牵出了一个问题,有效个数-1只会让最后一本图书不显示,当删除的书不是最末尾的书呢,所以这里可以将问题转换,如果要删除其中一本,只要让这本和最末尾的书进行交换即可
java
package library.operation;
import library.BookList;
import java.util.Scanner;
public class DelOperation implements IOperation{
@Override
public void work(BookList bookList) {
Scanner scanner = new Scanner(System.in);
System.out.println("输入要删除的图书序号");
//创建变量 index 来记录要删除的图书的序号
int index = scanner.nextInt();
//判断一下序号是否在范围内
if(index<0 || index>=bookList.getSize()){
System.out.println("序号有误");
return ;
}
//判断删除的序号是不是最后一本
if(index == bookList.getSize() -1){
//如果是最后一本,就把 Size-1
bookList.setSize(bookList.getSize() -1);
}else {
//如果不是最后一本,就要把最后一本复制到删除的序号位置
// 修改图书(要修改的下标,要修改的图书 (最后一本书的下标))
bookList.setBooks(index,bookList.getBook(bookList.getSize()-1));
//最后也要让 Size-1
bookList.setSize(bookList.getSize() -1);
}
System.out.println("删除成功");
}
}
删除前:
进行删除:
删除后:
原来在最后的福尔摩斯探案集就来到原来高等数学的位置,这样就算删除成功了
8.5 BorrowOperation
借阅只需要修改单个属性即可
java
package library.operation;
import library.Book;
import library.BookList;
import java.util.Scanner;
public class BorrowOperation implements IOperation{
@Override
public void work(BookList bookList) {
Scanner scanner = new Scanner(System.in);
System.out.println("输入要借阅的图书序号");
int index = scanner.nextInt();
//这里将我们要借阅的图书实例化,才可以修改它的 Borrow
Book book = bookList.getBook(index);
//因为Borrwed属性是布尔值,所以不需要去写 == true
if(book.getBorrowed()){
System.out.println("该书已被借阅");
}else {
//借阅成功后修改属性
book.setBorrowed(true);
System.out.println("借阅成功");
}
}
}
借阅过程:
借阅结果:
8.6 ReturnOperation
归还图书也差不多,只需要修改一些内容
java
package library.operation;
import library.Book;
import library.BookList;
import java.util.Scanner;
public class ReturnOperation implements IOperation{
@Override
public void work(BookList bookList) {
Scanner scanner = new Scanner(System.in);
System.out.println("输入要归还的图书序号");
int index = scanner.nextInt();
Book book = bookList.getBook(index);
if(book.getBorrowed()){
book.setBorrowed(false);
System.out.println("归还成功");
}else {
System.out.println("该书未被借阅");
}
}
}
8.7 FindOperation
这里写简单一些,只通过书名来查找书的序号
java
package library.operation;
import library.Book;
import library.BookList;
import java.util.Scanner;
public class FindOperation implements IOperation{
@Override
public void work(BookList bookList) {
//输入姓名
Scanner scanner = new Scanner(System.in);
String name = scanner.next();
//对所有图书进行遍历
for(int i = 0;i< bookList.getSize();i++){
//对图书进行实例化
Book book = bookList.getBook(i);
//判断字符串是否相等需要用到equals
if(book.getName().equals(name)){
//相等则打印书籍信息
System.out.println(book);
}
}
System.out.println("查找结束");
}
}
完