Java实现球员管理系统(逻辑篇)

球员管理系统,我们需要完成的功能如下。

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
	}
}

想直接看源代码的,可以去我另一篇,代码篇



















  • 8
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值