球员管理系统,我们需要完成的功能如下。
1. 查看所有球员
2. 雇佣新球员
3. 通过ID解雇球员
4. 通过ID修改球员信息
5. 通过ID查看球员信息
6.按照球员信息排序
先分析一下,需要的类。
1. 球员类
2. 管理球员的类,这里我们方便起见,就定义为球队管理类
3. 系统主界面类,也就是主类
暂时需要这些基础类,可以,先定义一下其中最基本的方法和属性。
Player类:
基本属性:
id,这里我们设置每添加一个球员就自动为其生成一个id属性
所以我们还需要另一个数,PLAYER_ID,设置其为static类型,每添加一个球员,就自动加1,并给球员赋值
name,球员的姓名
gender,球员的性别
age,球员的年龄
salary,球员的工资
location,球员的位置
方法:
只自己重写Object类继承而来的toString方法,方便展示其数据
这样,我们就可以定义一个最简单的球员类,当然我,我们要用到封装技术,所以这些属性全部私有化,并为其提供getter和setter方法。
构造方法我们只提供无参和全部参数两种,简单化。
代码如下:
package com.canmeng.entity;
public class Player {
private static int PLAYER_ID = 0;
private int id;
private String name;
private char gender;
private int age;
private double salary;
private String location;
{
PLAYER_ID++;
}
//构造方法
public Player() {}
public Player(String name, char gender, int age, double salary, String location) {
this.id = PLAYER_ID;
this.name = name;
this.gender = gender;
this.age = age;
this.salary = salary;
this.location = location;
}
//getter 和 setter 方法
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
@Override
public String toString() {
return "Player [id=" + id + ", name=" + name + ", gender=" + gender + ", age=" + age + ", salary=" + salary
+ ", location=" + location + "]";
}
}
球队管理类,在里面储存球员的信息,我们可以使用两种方法,一种是数组线性表,另一种是集合链性表。
但我们使用数组,方便理解。
默认一个球队在初始时只可以容纳10名球员,在之后添加球员时可以扩容。
先定义球队管理类的基本属性和方法。
基本属性:
name,球队的名字
allplayers [],球员数组,用于存放所有的球员
DEFAULT_CAPACITY,默认的球队可容纳球员个数,并设置其为final类型,方便维护
itemCount,统计个数,记录当前allplayers数组中有多少有效元素
代码如下:
package com.canmeng.function;
public class TeemManager {
private String name;
//保存Player类对象
private Player[] allPlayers = new Player[DEFAULT_CAPACITY];
//统计计数,统计当前allPlayers数组有多少元素
private static int itemCount = 0;
//默认的球队球员可容纳个数,方便维护
private static final int DEFAULT_CAPACITY = 10;
private static Scanner scanner = new Scanner(System.in);
//构造方法
public TeemManager() {}
public TeemManager(String name) {
this.name = name;
}
//getter 和 setter 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
当然,这只是最基础的属性类,我们需要完善里面的方法。
但是注意,在MVC设计模式下,我们要确保代码中不出现System.out.println打印语句
而是在视图界面,也就是前端与后端交界处,设计一个接口,他们之间明白彼此需要做的事,但怎么实现,就是前端需要考虑的
所以这时我们需要一个接口,来保存我们在程序中需要的打印语句接口
定义为TMVviewer,而其实现它的对象TMVviewerImpl
我们在teemManager里面定义接口的类用TMVviewerImpl作为实例
private static TMViewer tmViewer = new TMViewerImpl();
它们具体的内容我们要在之后的方法慢慢添加
首先,我们需要完成的方法,查看所有球员
当然,这很简单,但我们发现其中我们需要用到System.out.println语句,所以我们用TMVviewer封装
在接口中定义一个方法,名为showPlayer,参数为Player
package com.canmeng.viewer;
import com.canmeng.entity.Player;
public interface TMViewer {
public void showPlayer(Player temp);
}
这时我们就可以完成这个展示方法了,见代码:
package com.canmeng.viewer.impl;
import com.canmeng.entity.Player;
import com.canmeng.viewer.TMViewer;
public class TMViewerImpl implements TMViewer {
public void showPlayer(Player temp) {
System.out.println(temp);
}
}
再通过tmViewer这个对象使用这个方法
public void showAllPlayers() {
for (int i = 0; i < itemCount; i++) {
tmViewer.showPlayer(this.allPlayers[i]);
}
}
好,这个方法只是让我们练练手,了解基本格式
接下来,我们要解决的方法,雇佣新球员
但,问题来了,这个方法的参数是什么呢
我们在之前定义了一个数组,用于存放所有球员的
类型是Player,那么我们就将Player对象插入到数组的有效位置下标+1
好,有没有考虑一个问题,既然我们是传入Player对象,有没有问题呢
比如传入时,没有给它分配空间,导致是一个空对象
那么这时,就是一个异常,空的对象是不能操作它放置数组有效元素末尾的
这时我们自定义一个异常,名NullPlayerException
package com.canmeng.myexception;
public class NullPlayerException extends Exception {
private static final long serialVersionUID = 1L;
public NullPlayerException(String message) {
super(message);
}
}
还有一个问题是不是,比如我们插入时发现空间不够了怎么办,毕竟在程序的开始,我们只给了它10个空间
但可能插入的不止10个球员,这时我们就需要扩容,对的,扩容
前面我们知道,数组一旦定义成功,就不能更改其空间长度,那么怎么扩容呢
我们定义一个新数组,空间为之前的1.5倍,将其赋值给老数组,就可以了
放心,Java有垃圾回收机制,所以我们不用担心浪费空间
好,上代码
private void grow() {
//1. 获取原本的数组容量
int oldCapacity = this.allPlayers.length;
//2. 定义新的数组容量,是原本数组容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//3. 创建新的数组
Player[] newArray = new Player[newCapacity];
//4. 拷贝数据
for (int i = 0; i < oldCapacity; i++) {
newArray[i] = this.allPlayers[i];
}
this.allPlayers = newArray;
}
这里我解释一下,中间那个oldCapacity>>1是什么意思
>>是右移1位,是对于机器码来说,也就是数据在计算机中储存的补码,右移一位
也就是我们一般来说的/2,在底层来说,这样更快,而且更装逼
好,现在我们做完了扩容操作,可以雇佣球员了
public boolean employNewPlayer(Player newPlayerToAdd) throws NullPlayerException{
//参数合法性判断
if (newPlayerToAdd == null) {
throw new NullPlayerException("球员信息为空");
}
if (itemCount == this.allPlayers.length) {
grow();
}
//添加球员到Player数组中,有效元素末尾
this.allPlayers[itemCount++] = newPlayerToAdd;
return true;
}
接下来我们要完成的方法啦,就是通过ID解雇球员
我们知道,通过ID解雇球员,前提是有ID可以找到对应的球员,所以我们需要通过ID先找到对应的球员
问题又来了,传入ID不合法怎么办,异常需要定义一个了
package com.canmeng.myexception;
public class PlayerIDException extends Exception{
private static final long serialVersionUID = 1L;
public PlayerIDException(String message) {
super(message);
}
}
然后我们可以写那个根据ID找到对应球员下标的方法
private int findIndexByPlayerID(int playerID) throws PlayerIDException{
if(playerID < 0 || playerID > 100) {
throw new PlayerIDException("传入球员ID不合法!");
}
int index = -1;
for (int i = 0; i < itemCount; i++) {
if(this.allPlayers[i].getId() == playerID) {
index = i;
break;
}
}
return index;
}
那么我们找到了下标该怎么操作呢,是不是有没有找到的情况
这时就需要输出语句提示用户,查无此人了
之前我们提到了,在我们的业务逻辑中,不能出现前端代码,也就是打印语句
需要用到接口与前端交互,TMVviewer中添加一个输出语句,代码如下:
public void deleteError();
删除错误,也就是查无此人,我们需要在TMVviewerImpl中实现该方法
public void deleteError() {
System.out.println("查无此人,无法删除");
}
这样,我们就可以在解雇球员代码中,完善这些功能了
public boolean layoffPlayerByID(int playerID) throws PlayerIDException {
int index = findIndexByPlayerID(playerID);
if (index != -1) {
for (int i = index; i < itemCount - 1; i++) {
allPlayers[i] = allPlayers[i + 1];
}
allPlayers[--itemCount] = null;
return true;
}else {
//使用界面接口来完成展示
tmViewer.deleteError();
return false;
}
}
接下来我们要完成的功能就是通过球员ID号更改球员信息
当然,我们之前已经完成了通过ID查找球员对应下标的方法
里面还有些逻辑我们需要理清,更改球员信息,应该给用户一个更改界面
方便用户选择,需要展示语句,同时也需要相应的球员信息,故也需要参数
public void modifyPlayerInfo(Player temp);
完成方法
public void modifyPlayerInfo(Player temp) {
System.out.println("修改" + temp.getName() + "球员信息:");
System.out.println("1. 修改球员姓名 ");
System.out.println("2. 修改球员性别 ");
System.out.println("3. 修改球员年龄 ");
System.out.println("4. 修改球员工资 ");
System.out.println("5. 修改球员位置 ");
System.out.println("6. 结束修改 ");
}
每一个选项选择后,都可以通过打印语句来提示用户输入
public void PleaseInputName();
public void PleaseInputGender();
public void GenderError()
public void PleaseInputAge();
public void PleaseInputSalary();
public void PleaseInputLocation();
public void modifyComplete();
我们再在它所实现的类中编写输出语句
public void PleaseInputName() {
System.out.println("请输入球员姓名:");
}
public void PleaseInputGender() {
System.out.println("请输入球员性别:");
}
public void GenderError() {
System.out.println("输入错误,默认性别为男");
}
public void PleaseInputAge() {
System.out.println("请输入球员年龄:");
}
public void PleaseInputSalary() {
System.out.println("请输入球员工资:");
}
public void PleaseInputLocation() {
System.out.println("请输入球员位置:");
}
public void modifyComplete() {
System.out.println("修改完成 退出.");
}
基本逻辑其实已经很清楚了,先通过传入ID找到对应球员的下标
在打印语句让用户选择修改选项
代码如下
public void modifyPlayerById(int playerID) throws PlayerIDException{
int index = findIndexByPlayerID(playerID);
if(index != -1) {
int flag = -1;
//作为一个临时变量
Player temp = this.allPlayers[index];
while (true) {
tmViewer.modifyPlayerInfo(temp);
int choose = scanner.nextInt();
scanner.nextLine();
//利用switch-case结构完成修改
switch (choose) {
case 1:
tmViewer.PleaseInputName();
String name = scanner.nextLine();
temp.setName(name);
break;
case 2:
tmViewer.PleaseInputGender();
char gender = scanner.nextLine().charAt(0);
//过滤数据
if (gender == '男' || gender == '女') {
temp.setGender(gender);
}else {
tmViewer.GenderError();
temp.setGender('男');
}
break;
case 3:
tmViewer.PleaseInputAge();
int age = scanner.nextInt();
if (age > 0 || age < 45) {
temp.setAge(age);
}else {
temp.setAge(18);
}
break;
case 4:
tmViewer.PleaseInputSalary();
double salary = scanner.nextDouble();
if (salary > 0) {
temp.setSalary(salary);
}else {
temp.setSalary(8000);
}
break;
case 5:
tmViewer.PleaseInputLocation();
String location = scanner.nextLine();
temp.setLocation(location);
break;
case 6:
flag = 1;
tmViewer.modifyComplete();
break;
default:
break;
}//switch
if (flag == 1) {
break;
}
}//while true
}else {
}
}
通过ID展示球员信息,这就很简单了
里面需要的方法在之前都已经定义过了
public void showPlayerByID(int playerID) throws PlayerIDException{
int index = findIndexByPlayerID(playerID);
if (index != -1) {
tmViewer.showPlayer(this.allPlayers[index]);
}
}
最后一个需要完成的方法了
按照球员信息排序,让我们看看,球员有什么信息
id,age,salary,这些还可以按照升序降序排序
也就是说,如果写方法的话,我们要写六个方法
而且这六个方法只有判断条件不同,是不是很占空间
我们可以定义一个公用的排序算法,在里面判断时,就调用我们写的一个接口的判断方法
也就是说,我们判断时只用看接口里面实现的方法,那我们是不是只用写一个算法了呢
先定义一个比较器接口,用于判断
package com.canmeng.mycompare;
import com.canmeng.entity.Player;
/**
* 这里是自定义的比较器接口,里面规定比较的方式,应该使用哪一个方法
* @author CanMeng
*
*/
public interface MyCompare {
/**
* 这里是自定义的比较方式,要求所有遵从该接口的类,都要完成这个方法
* @param p1
* @param p2
* @return boolean类型,根据实际情况判断其含义
*/
public boolean compareTwoPlayer(Player p1, Player p2);
}
在我们调用排序算法时,在重写里面的方法,不同属性之间的比较
当然,我们这里排序算法用的是选择排序
public void selectSortUsingMyCompare(MyCompare compare) {
//数据保护,排序数据拿出来,拷贝一份
Player[] sortTemp = new Player[itemCount];
for (int i = 0; i < itemCount; i++) {
sortTemp[i] = this.allPlayers[i];
}
//选择排序算法
for(int i = 0; i < itemCount - 1; i++) {
int index = i;
for(int j = i; j < itemCount; j++) {
if (compare.compareTwoPlayer(sortTemp[index], sortTemp[j])) {
index = j;
}
}
if (index != i) {
Player temp = sortTemp[index];
sortTemp[index] = sortTemp[i];
sortTemp[i] = temp;
}
}
//展示效果
for (int i = 0; i < itemCount; i++) {
System.out.println(sortTemp[i]);
}
}
好,这些方法我们都已经完成了
该写出主界面来供用户观看了
方便来看,我们先写一个实现模拟清屏功能的方法
public void ClearScreen();
public void ClearScreen() {
for (int i = 0; i < 20; i++) {
System.out.println();
}
}
主菜单界面
public void menuViewer();
public void menuViewer() {
System.out.println("欢迎来到实力至上主义的教室");
System.out.println("**************************************");
System.out.println("$ 1. 查看所有球员 $");
System.out.println("$ 2. 雇佣新球员 $");
System.out.println("$ 3. 通过ID解雇球员 $");
System.out.println("$ 4. 通过ID修改球员信息 $");
System.out.println("$ 5. 通过ID查看球员信息 $");
System.out.println("$ 6. 按照球员信息排序 $");
System.out.println("$ 7. 退出 $");
System.out.println("**************************************");
}
提示用户输入ID
public void PleaseInputID();
public void PleaseInputID() {
System.out.println("请输入球员ID:");
}
排序界面
public void menuViewerBySort();
public void menuViewerBySort() {
System.out.println("**************************************");
System.out.println("$ 1. 根据ID排序 $");
System.out.println("$ 2. 根据年龄排序 $");
System.out.println("$ 3. 根据工资排序 $");
System.out.println("$ 4. 退出 $");
System.out.println("**************************************");
}
排序子界面选择升序降序
public void menuViewerBySortAscendingOrDecending();
public void menuViewerBySortAscendingOrDecending() {
System.out.println("**************************************");
System.out.println("$ 1. 升序 $");
System.out.println("$ 2. 降序 $");
System.out.println("**************************************");
}
提示用户输入回车键继续
public void PromptEnterKeyToContinue();
public void PromptEnterKeyToContinue() {
System.out.println("输入回车键以继续......");
}
好,界面部分我们都已经完成了
贴代码
package com.canmeng.domain;
import java.util.Scanner;
import com.canmeng.entity.Player;
import com.canmeng.function.TeemManager;
import com.canmeng.mycompare.MyCompare;
import com.canmeng.myexception.NullPlayerException;
import com.canmeng.myexception.PlayerIDException;
import com.canmeng.viewer.TMViewer;
import com.canmeng.viewer.impl.TMViewerImpl;
public class MainProject {
public static void main(String[] args) throws PlayerIDException, NullPlayerException {
//用户界面选择
int choose = 0;
TMViewer TMV = new TMViewerImpl();
Scanner scanner = new Scanner(System.in);
TeemManager teemManager = new TeemManager("canmeng");
for (int i = 0; i < 10; i++) {
String name = "CanMeng" + i;
char gender = '男';
int age = (int) (Math.random() * 100);
double salary = Math.random() * 10000;
String location = "CanMengMeng";
Player newPlayerToAdd = new Player(name, gender, age, salary, location);
teemManager.employNewPlayer(newPlayerToAdd);
}
while (true) {
TMV.ClearScreen();
//主界面
TMV.menuViewer();
choose = scanner.nextInt();
scanner.nextLine();
TMV.ClearScreen();
int flag = -1;
switch (choose) {
case 1:
//查看所有球员
teemManager.showAllPlayers();
break;
case 2:
//雇佣新球员
TMV.PleaseInputName();
String name = scanner.nextLine();
TMV.PleaseInputGender();
char gender = scanner.nextLine().charAt(0);
TMV.PleaseInputAge();
int age = scanner.nextInt();
TMV.PleaseInputSalary();
double salary = scanner.nextDouble();
scanner.nextLine();
TMV.PleaseInputLocation();
String location = scanner.nextLine();
Player newPlayerToAdd = new Player(name, gender, age, salary, location);
teemManager.employNewPlayer(newPlayerToAdd);
break;
case 3:
//通过ID解雇球员
TMV.PleaseInputID();
int playerID = scanner.nextInt();
teemManager.layoffPlayerByID(playerID);
break;
case 4:
//通过ID修改球员信息
TMV.PleaseInputID();
playerID = scanner.nextInt();
teemManager.modifyPlayerById(playerID);
break;
case 5:
//通过ID查看球员信息
TMV.PleaseInputID();
playerID = scanner.nextInt();
teemManager.showPlayerByID(playerID);
break;
case 6:
//按照球员信息排序,需展示其子界面
TMV.menuViewerBySort();
choose = scanner.nextInt();
scanner.nextLine();
TMV.ClearScreen();
switch (choose) {
case 1:
//根据ID排序
TMV.menuViewerBySortAscendingOrDecending();
choose = scanner.nextInt();
if (choose == 1) {
teemManager.selectSortUsingMyCompare(new MyCompare() {
@Override
public boolean compareTwoPlayer(Player p1, Player p2) {
return p1.getId() > p2.getId();
}
});
} else if (choose ==2) {
teemManager.selectSortUsingMyCompare(new MyCompare() {
@Override
public boolean compareTwoPlayer(Player p1, Player p2) {
return p1.getId() < p2.getId();
}
});
}else {
break;
}
case 2:
//根据年龄排序
TMV.menuViewerBySortAscendingOrDecending();
choose = scanner.nextInt();
if (choose == 1) {
teemManager.selectSortUsingMyCompare(new MyCompare() {
@Override
public boolean compareTwoPlayer(Player p1, Player p2) {
return p1.getAge() > p2.getAge();
}
});
} else if (choose ==2) {
teemManager.selectSortUsingMyCompare(new MyCompare() {
@Override
public boolean compareTwoPlayer(Player p1, Player p2) {
return p1.getAge() < p2.getAge();
}
});
}else {
break;
}
case 3:
//根据工资排序
TMV.menuViewerBySortAscendingOrDecending();
choose = scanner.nextInt();
if (choose == 1) {
teemManager.selectSortUsingMyCompare(new MyCompare() {
@Override
public boolean compareTwoPlayer(Player p1, Player p2) {
return p1.getSalary() > p2.getSalary();
}
});
} else if (choose ==2) {
teemManager.selectSortUsingMyCompare(new MyCompare() {
@Override
public boolean compareTwoPlayer(Player p1, Player p2) {
return p1.getSalary() < p2.getSalary();
}
});
}else {
break;
}
case 4:
break;
default:
break;
}
break;
case 7:
flag = 1;
break;
default:
break;
}//switch
if (flag != -1) {
break;
}else {
TMV.PromptEnterKeyToContinue();
new Scanner(System.in).nextLine();
}
}//while true
}
}
想直接看源代码的,可以去我另一篇,代码篇