文章目录
《C++转Java快速入手系列》实践篇:图书系统
在之前的几篇博客写了很多java语法的特性,为了加深理解,这期来做一个简易项目,从设计思路到语法使用技巧一步一步构建,希望各位有所收获。
1、类设计
图书系统项目,顾名思义就要提供用户来进行借阅书籍的流程,想象你面前摆着一本书:《Effective Java》。它有什么?一个书名、一位作者、书的价格,还有一个状态:此刻它是安静地躺在书架上,还是已经被某位读者借走了?再想象图书馆里中的人:张三,他是用户还是图书管理员,手里可能正拿着那本《Effective Java》。他的身份决定了他可能做出的操作行为,当他是图书管理员的时候,可能是要为书架增添书籍,或者下架某本书,当他是用户的时候,可能是决定借书或还书,这些操作同样也会改变书架或者书的状态,这些变化必须被我们的系统捕捉到。
在 Java 的世界里,这些真实存在的事物很容易建模:
把"书"抽象成 Book 类,包含name、author、price 、type 和 isborrowed 属性;
把"人"抽象成 User 类,包含 name属性;
把"书架"再设计一个 BookList 类,作为整个系统的"交互枢纽",包含书的数组Book[] 、存放书的个数:usedSize
把"操作"抽象成 IOperations接口用来将不同的操作统一路径方便调用和管理
根据最初的设想,我们先分出这么几种类,接着我们走一步看一步进行补充
为了管理方便,我们还可以创建三个包来分类:book包, user包,ioperations包:

2、book包
book包中含有Book类和BookList类
2.1、Book类
Book类中的属性在上面已经提出来了,除此之外我们为了后面方便访问Book类中的元素,我们把每个属性的get和set方法都写出来,有多余的最后进行删除也可以。
前面的get和set都是可以在此时此刻想到的,如果你想,也可以高瞻远瞩一下后面的增加书籍 和查看书籍的操作,进而想到下面两点的内容,即使想不到也不要紧,后面需要的时候再回来补充也不迟,这里为了避免代码混乱,提前抛出需求写出完整版:
- 增加书籍的操作,需要要对Book对象进行构建,仅仅只有默认构造是很不方便的,所以我们写出对应的构造函数(isborrowed不需要传入赋值,默认即为false不被借阅,很合理)
- 查看书籍的操作,需要对Book类进行打印出来,所以需要我们重写toString方法
java
package book;
public class Book {
private String name;
private String author;
private int price;
private String type;
private boolean isBorrowed;
public String getName() {
return name;
}
public String getAuthor() {
return author;
}
public int getPrice() {
return price;
}
public String getType() {
return type;
}
public boolean isBorrowed() {
return isBorrowed;
}
public void setBorrowed(boolean borrowed) {
isBorrowed = borrowed;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
", type='" + type + '\'' +
((isBorrowed == true) ? "已借出":"未借出") +
'}';
}
public Book(String name, String author, int price, String type) {
this.name = name;
this.author = author;
this.price = price;
this.type = type;
}
}
2.2、BookList类
该类的属性再上方已提出,除此之外需要对这些属性进行初始化,为了方便查询,我们默认该书架有三本书 对此来写构造方法,对这些属性的get和set方法也要写
- 我们再后面的许多操作中也需要通过遍历booklist数组来找书,所以需要传入下标来访问书架中的书,所以需要getBook(int pos) 和setBook(int pos,Book book)方法
java
package book;
public class BookList {
private Book[] booklist = new Book[10];
private int usedSize;
public BookList() {
this.booklist[0] = new Book("三国演义","罗贯中",10,"小说");
this.booklist[1] = new Book("西游记","吴承恩",59,"小说");
this.booklist[2] = new Book("红楼梦", "曹雪芹", 16, "小说");
this.usedSize = 3;
}
public Book getBook(int pos) {
return booklist[pos];
}
public Book[] getBooklist() {
return booklist;
}
public void setBook(int pos, Book book) {
this.booklist[pos] = book;
}
public int getUsedSize() {
return usedSize;
}
public void setUsedSize(int usedSize) {
this.usedSize = usedSize;
}
}
3、user包
在这个包中除了User类,我们还需要针对不同身份的人来显示出不同的菜单来选择可操作的选择,所以这里我们可以再设计两个User子类,NormalUser和AdminUser
3.1、User类
User类在这里并不是指代具体的某个用户,所以它的作用就是为了实现多态,那么该类我们可以设计成抽象类,对不同的用户我们给出不同的菜单,这个菜单就是抽象方法
- 这里的菜单的返回值为什么是int我们后面再对其进行分析
- 后面我们还需要针对菜单中不同的选项,做出不同的操作,我们也需要在用户的多态上做文章,所以User类还需要一个抽象方法:doIoperation(int choice,BookList bookList);
java
package user;
import book.BookList;
public abstract class User {
protected String name;
public User(String name) {
this.name = name;
}
public abstract int menu();
public abstract void doIoperation(int choice, BookList bookList);
}
3.2、NormalUser类
作为继承了User的类,该类必须要对父类中的抽象方法进行重写,该方法的目的是传入对应的选择,对应要做出某些行动,为了区分NormalUser 和 AdminUser这两种用户之间的选择,通过创建一个操作类数组来分别初始化不同的操作对象来做到区分。
java
package user;
import book.BookList;
import ioperations.*;
import java.util.Scanner;
public class NormalUser extends User {
IOperations[] iOperations;
public NormalUser(String name) {
super(name);
//这里初始化的赋值也是一次向上转型
iOperations = new IOperations[]{new ExitOpertaion(),new FindOperation(),new BorrowOperation(),new ReturnOperation()};
}
public int menu(){
System.out.println("欢迎来到" + this.name + "的图书系统");
System.out.println("******** 普通用户菜单 ********");
System.out.println("1、查找图书");
System.out.println("2、借阅图书");
System.out.println("3、归还图书");
System.out.println("0、退出系统");
System.out.println("请输入你的操作:");
Scanner scanner = new Scanner(System.in);
int choice = scanner.nextInt();
return choice;
}
public void doIoperation(int choice,BookList bookList){
iOperations[choice].work(bookList);
}
}
3.3、AdminUser类
同理,这边也是针对于管理员应该看到的菜单和做出的操作选项来进行重写
java
package user;
import book.BookList;
import ioperations.*;
import java.util.Scanner;
public class AdminUser extends User{
protected IOperations[] iOperations;
public AdminUser(String name) {
super(name);
iOperations = new IOperations[]{new ExitOpertaion(),new FindOperation(), new AddOperation(), new DelOperations(), new ShowOperation()};
}
public int menu(){
System.out.println("******* 管理员菜单 ******");
System.out.println("1、查找图书");
System.out.println("2、新增图书");
System.out.println("3、删除图书");
System.out.println("4、显示图书");
System.out.println("0、退出系统");
System.out.println("***********************");
Scanner scanner = new Scanner(System.in);
System.out.println("请输入你的操作:");
int choice = scanner.nextInt();
return choice;
}
public void doIoperation(int choice,BookList bookList){
iOperations[choice].work(bookList);
}
}
4、Main类
整个系统的进行流程就从这个类开始
- 先实现登录系统,来确定用户类型,如何确定并返回呢,这里就要用到多态的返回值向上转型:
- 根据返回的User类型的对象,通过动态绑定来访问其对应的menu()方法
- main方法的作用是让用户选择对应的操作,所以说该方法的返回值是int.
- 根据返回的选择并且构建一个 bookList对象然后再次进行一次动态绑定的调用doIoperation()方法
- 最后让整个过程可循环即可
java
import book.BookList;
import ioperations.IOperations;
import user.User;
import user.AdminUser;
import java.util.Scanner;
import user.NormalUser;
public class Main {
public static User login(){
Scanner scanner = new Scanner(System.in);
System.out.println("请输入你的姓名:");
String name = scanner.next();
System.out.println("请输入你的身份:");
System.out.println("1、管理员 2、普通用户");
int choice = scanner.nextInt();
if(choice == 1){
return new AdminUser(name);
}else{
return new NormalUser(name);
}
}
public static void main(String[] args) {
// 1、先针对不同的用户类型来给出不同的菜单进行选择
User user = login();
while (true){
int choice = user.menu();
//2、给出菜单然,用户选择后返回选项,要针对不同的用户来判断具体的选项并且执行,只能是通过多态动态绑定实现
BookList bookList = new BookList();
user.doIoperation(choice,bookList);
}
}
}
5、ioperations包
这个包的创建为了管理许多各种各样的操作类,其中还包括Iorerations接口
5.1、IOperations接口
因为这里所有的操作类种类繁多,如果不实现多态,那么这些方法调用逻辑就要依托于大量的繁琐的if else语句。而又因为这里的IOperations和众多的操作类的关系并不是 is 的关系,且并没有相同的成员变量,也没有共享任何的代码,这里的复用优势也用不上,还为了降低耦合度,我们这里不选择用抽象类,而是用接口
- 该接口提供一个操作的抽象方法即可
java
package ioperations;
import book.BookList;
public interface IOperations {
public void work(BookList bookList);
}
5.2、AddOperation类
增加书籍操作
业务逻辑分为以下几步:
- 判断书架是否已满
- 构建新书对象
- 判断书架有没有这本书
- 插入这本书
java
package ioperations;
import book.Book;
import book.BookList;
import java.util.Scanner;
public class AddOperation implements IOperations{
public void work(BookList bookList){
// 判断数组是否已满
int size = bookList.getUsedSize();
if(size == bookList.getBooklist().length){
System.out.println("书架满了,不能放了");
}
//2、构建新书对象
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("请输入价格");
int price = scanner.nextInt();
System.out.println("请输入书的类型");
String type = scanner.next();
Book newbook = new Book(name,author,price,type);
//3、判断书架有没有这本书
for (int i = 0; i < size; i++) {
Book book = bookList.getBook(i);
if(book.getName().equals(newbook)){
System.out.println("有这本书,不能新增图书");
return;
}
}
//4、插入这本书
bookList.setBook(size,newbook);
bookList.setUsedSize(size + 1);
System.out.println("新增图书成功");
}
5.3、BorrowOperation类
借阅书籍操作
业务逻辑分为以下几步:
1.获取需要借阅书籍的书名
2.遍历书架找到对应书名看书架中存不存在这本书
3.书籍存在时判断该书的状态是否被借出
java
package ioperations;
import book.Book;
import book.BookList;
import java.util.Scanner;
public class BorrowOperation implements IOperations {
public void work(BookList bookList){
System.out.println("借阅图书");
Scanner scanner = new Scanner(System.in);
System.out.println("请输入你借阅的书名");
String name = scanner.nextLine();
int size = bookList.getUsedSize();
for (int i = 0; i < size; i++) {
Book book = bookList.getBook(i);
if(book.getName().equals(name)){
if(book.isBorrowed() == true){
System.out.println("这本书已经被借出了");
return;
}
book.setBorrowed(true);
System.out.println("借阅成功");
return;
}
}
System.out.println("没有你要借阅的这本书");
}
}
5.4、DelOperation类
删除书籍操作
业务逻辑分为以下几步:
- 获取需要删除的书名
- 遍历书架看书架中是否存在该书
- 如果存在返回其对应下标进行删除(与顺序表的删除逻辑相同)
java
package ioperations;
import book.Book;
import book.BookList;
import java.util.Scanner;
public class DelOperations implements IOperations {
public void work(BookList bookList){
System.out.println("删除图书");
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要删除的书名");
String name = scanner.next();
int size = bookList.getUsedSize();
int pos = -1;
int i = 0;
for (; i < size ; i++) {
if (bookList.getBook(i).equals(name)){
System.out.println("找到了这本书");
pos = i;
break;
}
}
if(pos == size){
System.out.println("没有要删除的书");
return;
}
//这里开始删除
for (int j = pos; j < size - 1; j++) {
//java中只有数组才能用下标
//bookList.getBooklist()[j] = bookList.getBooklist()[j + 1];
Book book = bookList.getBook(j + 1);
bookList.setBook(j,book);
}
bookList.setUsedSize(size - 1);
}
}
5.5、ExitOperation类
退出系统操作
- 使用System中的exit()操作来退出即可
java
package ioperations;
import book.BookList;
public class ExitOperation implements IOperations {
public void work(BookList bookList){
System.out.println("退出系统");
System.exit(0);
}
}
5.6、FindOperation类
查找图书功能
业务逻辑分为以下几步:
- 获取想要查找的书名
- 遍历书架判断是否存在于书架中
- 如果存在则返回找到了,并且打印出该书的所有属性(Book类要重写toString()方法)
java
package ioperations;
import book.Book;
import book.BookList;
import java.util.Scanner;
public class FindOperation implements IOperations {
public void work(BookList bookList){
System.out.println("查找图书,请输入你的书名");
Scanner scanner =new Scanner(System.in);
String name = scanner.next();
int size = bookList.getUsedSize();
for (int i = 0; i < size; i++) {
Book book = bookList.getBook(i);
if(book.getName().equals(name)){
System.out.println("找到了这本书");
System.out.println(book);
return;
}
}
System.out.println("没有你要找的这本书");
}
}
5.7、ReturnOperation类
归还书籍操作
业务逻辑分为以下几步:
- 获取要归还书籍的书名
- 遍历书架看是否存在该书
- 如果存在则判断该书的isBorrowed属性是否被借出
- 如果被借出了则归还成功,如果没有被借出则返回归还失败
java
package ioperations;
import book.Book;
import book.BookList;
import java.util.Scanner;
public class ReturnOperation implements IOperations {
public void work(BookList bookList){
System.out.println("归还图书");
Scanner scanner = new Scanner(System.in);
System.out.println("请输入你归还的书名");
String name = scanner.next();
int size = bookList.getUsedSize();
for (int i = 0; i < size; i++) {
Book book = bookList.getBook(i);
if(book.getName().equals(name)){
if (book.isBorrowed()){
book.setBorrowed(false);
System.out.println("归还成功!");
return;
}
}
}
System.out.println("没有你要归还的图书!");
}
}
5.8、ShowOperation类
展示书架操作
- 遍历书架打印所有的书籍属性即可(Book类要重写toString()方法)
java
package ioperations;
import book.Book;
import book.BookList;
public class ShowOperation implements IOperations {
public void work(BookList bookList){
//展示需要遍历书架,遍历书架需要usedSize,所以要在bookList上提供get()set()方法
int size = bookList.getUsedSize();
for (int i = 0; i < size; i++) {
//要遍历书也得拿到数组
Book book = bookList.getBook(i);
System.out.println(book);
}
}
}
至此极简版的图书系统完成