一、任务概述
本练习用于熟悉快递管理业务,完成快递管理控制台项目,具体需求如下:
采用二维数组的方式存储快递
- 管理员
- 录入快递
- 柜子位置(系统产生,不能重复)
- 快递单号(输入)
- 快递公司(输入)
- 6位取件码(系统产生,不能重复)
- 删除快递(根据单号)
- 修改快递(根据单号)
- 查询所有快递(遍历)
- 录入快递
- 普通用户
- 快递取出
- 输入取件码:显示快递的信息 和 在哪个柜子中,从柜子中移除这个快递
- 快递取出
功能实现如下:
存快递:
删除快递:
修改快递:
查询所有快递:
取件:
此习题来自 开课吧:《新职课JavaEE软件开发工程师》课程
第三章:面向对象
第6节:异常处理
二、MVC设计模式
1、定义
MVC模式(Model–view–controller)是软件工程中的一种软件架构模式,它把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。
MVC模式中三个组件的详细介绍如下:
- 模型(Model):用于封装与应用程序的业务逻辑相关的数据以及对数据的处理方法。“Model”有对数据直接访问的权力,例如对数据库的访问。“Model”不依赖“View”和“Controller”,也就是说, Model 不关心它会被如何显示或是如何被操作;
- 视图(View):能够实现数据有目的的显示(理论上,这不是必需的)。在 View 中一般没有程序上的逻辑。为了实现 View 上的刷新功能,View 需要访问它监视的数据模型(Model),因此应该事先在被它监视的数据那里注册;
- 控制器(Controller):起到不同层面间的组织作用,用于控制应用程序的流程。它处理事件并作出响应。“事件”包括用户的行为和数据 Model 上的改变。
2、优点
a、低耦合
通过将视图层和业务层分离,允许更改视图层代码而不必重新编译模型和控制器代码,同样,一个应用的业务流程或者业务规则的改变,只需要改动MVC的模型层(及控制器)即可。因为模型与控制器和视图相分离,所以很容易改变应用程序的数据层和业务规则。
由于运用 MVC 的应用程序的三个部件是相互独立,改变其中一个部件并不会影响其它两个,所以依据这种设计思想能构造出良好的松耦合的构件。
b、复用性强
随着技术的不断进步,当前需要使用越来越多的方式来访问应用程序了。MVC模式允许使用各种不同样式的视图来访问同一个服务端的代码,这得益于多个视图(如WEB(HTTP)浏览器或者无线浏览器(WAP))能共享一个模型。
比如,用户可以通过电脑或通过手机来订购某样产品,虽然订购的方式不一样,但处理订购产品的方式(流程)是一样的。由于模型返回的数据没有进行格式化,所以同样的构件能被不同的界面(视图)使用。例如,很多数据可能用 HTML 来表示,但是也有可能用 WAP 来表示,而这些表示的变化所需要的是仅仅是改变视图层的实现方式,而控制层和模型层无需做任何改变。
由于已经将数据和业务规则从表示层分开,所以可以最大化的进行代码重用了。
三、思路梳理
1、MVC架构设计
a、Controller
严格按照MVC模式来设计架构,Controller的作用是流程控制,用来将数据和视图连结。所以Controller中只包含逻辑判断代码,调用Model、View的代码,不包含任何业务运算代码和视图类的代码。
Controller中应当包含快递程序的主入口、判断分支后的各类方法。
b、Model
所有的业务内容,数据存储,数据修改方法等都在Model当中,不与View有任何的耦合关系。此练习只做面向对象练习,所以采用二维数组和快递类对象的方式来储存快递,不使用数据库。
c、View
此练习只做面向对象和MVC设计模式练习,用户交互页面使用打印和Scanner获取。View 中包含所有打印的方法和获取用户输入的方法,不包含任何逻辑和业务代码。
2、流程控制
首先考虑多层分支结构,获取用户输入的信息,分层判断
3、快递信息存储
创建Model类型的10×10的二维数据,来代替10×10的快递柜,可以存放100个快递对象。
快递使用对象的方式来存储,对象中包含快递单号、快递公司和取件码3个属性。
管理员存入快递时创建快递对象,其中快递单号和快递公司由管理员键盘输入并赋值给对象,取件码需要随机生成,同时保证单号和取件码与其他快递不重复。
4、随机生成取件码
采用Random类中的random(int bound)方法来随机生成6位取件码,bound为随机数的边界,当bound为100000时,会生成0~999999之间的随机数,如下:
Random random = new Random();
int code = random.nextInt(1000000);
但因为是int类型的随机数,所以有可能出现随机数不是6位数的情况,例如随机到了278,显然我们想要的是6位的“000278”,同时我们生活中很多常见的取件码都不以0作为开头,所以可以用如下方案生成100000~999999之间的取件码:
int code = random.nextInt(900000)+100000;
在生成取件码之后,我们还需要添加去重功能,同时用户需要通过取件码来查找快递,所以还需要编写一个方法,通过取件码来查询快递。还需要通过快递单号来查询快递的方法,用户管理员增删修改快递。
5、查找快递
a、按快递单号查找
需要编写一个方法,通过快递单号来查询二维数组中对应的快递对象,管理员添加快递输入单号时可以查重,删除修改和遍历快递也需要用此方法。
可以通过双层for循环,遍历二维数组,取出其中的快递对象,调用对象的get()方法,获取它的快递单号,来和我们给方法输入的单号做对比。
b、按取件码查找
与上同理,需要编写通过取件码查询快递的方法,用于用户取件和管理员存快递生成取件码时去重。
6、删除与修改
有了查找快递的方法之后,用户取件、快递员删除和修改快递,都可以先查找到对象,再在model类中对对象进行相应操作即可。主体思路框架有了,接下来开始撸代码。
四、任务拆解与代码
1、创建model、view、controller三个类
a、view
视图类,只包含Scanner对象,和各种打印方法。
import java.util.Scanner;
public class ExpressView {
private final Scanner input = new Scanner(System.in);
// 后续编写所有的视图打印方法
}
b、model
创建保存快递对象的二维数组,创建对象中的变量信息,String类型的快递单号,快递公司,和int类型的取件码,并为参数生成get和set方法(取件码只有get没有set方法),生成类的构造方法。
另外创建一个int类型的变量size,用来记录快递柜中已存入快递的数量。
public class ExpressModel {
private ExpressModel express[][] = new ExpressModel[10][10];
private String expressNumber;
private String expressCompany;
private int expressCode;
private int size;
public ExpressModel() {}
public ExpressModel(String expressNumber, String expressCompany, int expressCode) {
this.expressNumber = expressNumber;
this.expressCompany = expressCompany;
this.expressCode = expressCode;
}
public String getExpressNumber() {
return expressNumber;
}
public void setExpressNumber(String expressNumber) {
this.expressNumber = expressNumber;
}
public String getExpressCompany() {
return expressCompany;
}
public void setExpressCompany(String expressCompany) {
this.expressCompany = expressCompany;
}
public int getSize() {
return size;
}
public int getExpressCode() {
return expressCode;
}
// 后续编写业务方法
}
c、controller
Controller中需要调度view和model,创建实例对象。包含快递程序的一切流程,创建一个主方法作为快递功能的主入口。
public class ExpressController {
ExpressView view = new ExpressView();
ExpressModel model = new ExpressModel();
public void expressMain() {}
}
2、视图类所有方法
视图方法可以随着Controller的编写逐个添加,这里为了方便下文阅读理解,提前列出视图类所有方法参阅。为保证程序正常运行,全部采用Scanner类中的nextLine()方法来接受用户输入的数据。
package pers.Enrico.ExpressMVC;
import java.util.Scanner;
public class ExpressView {
private final Scanner input = new Scanner(System.in);
public void printTitle() {
System.out.println("快递单号\t快递公司\t取件码");
}
public void printExpress(ExpressModel express) {
System.out.println(express.getExpressNumber()+"\t"+express.getExpressCompany()+"\t"+express.getExpressCode());
}
public void expressInEmpty() {
System.out.println("当前快递柜内无快递。");
}
public void welcome() {
System.out.println("\n===欢迎使用新职课快递柜===");
}
public void bye() {
System.out.println("===欢迎再次使用新职课快递柜===");
}
public String getUser() {
System.out.println("请输入您的身份:1-快递员,2-用户,0-退出");
return input.nextLine();
}
public String getCourierOperation() {
System.out.println("请选择操作:1-存快递 2-删除快递 3-修改快递信息 4-查看所有快递");
return input.nextLine();
}
public String getExpressCode() {
System.out.println("请输入取件码:");
return input.nextLine();
}
public void getExpressOver() {
System.out.println("取件成功!");
}
public void getExpressFail() {
System.out.println("查无此取件码对应的快递,请检查取件码。");
}
public String getExpressNumber() {
System.out.println("请输入快递单号:(输入0返回上一层)");
return input.nextLine();
}
public String getExpressCompany() {
System.out.println("请输入快递公司:");
return input.nextLine();
}
public void addExpressOver(int code) {
System.out.println("快递已成功存入!取件码为:"+code);
}
public void addExpressNumberError() {
System.out.println("该单号的快递已存入,请检查快递单号。");
}
public void addExpressFull() {
System.out.println("快递柜已满,无法存入");
}
public void updateExpressOver() {
System.out.println("快递信息已修改,新信息如下:");
}
public void delExpressOver() {
System.out.println("快递删除成功!");
}
public void expressNotExist() {
System.out.println("该单号的快递不存在,请检查快递单号。");
}
public void reInput() {
System.out.println("您输入的信息有误,请重新输入。");
}
}
3、主流程控制
Controller中只包含流程控制代码,实现视图功能需要调用view中的方法,修改数据功能需要调用model中的方法,具体方法之后逐步完善。
快递主入口,包含欢迎语和结束语,以及获取用户输入的信息来确定用户身份的方法,做进一步分支流程判断。
调用view.getUser()方法,提示用户输入身份信息: 0:退出 1:管理员 2:普通用户
将用户输入的数据转化为int类型,并用以判断用户身份。稍后完善changeStringToInt()方法。
public class ExpressController {
ExpressView view = new ExpressView();
ExpressModel model = new ExpressModel();
public void expressMain() {
while (true) {
view.welcome();
// 调用view.getUser()方法,提示用户输入身份信息:
// 将用户输入的数据转化为int类型,并用以判断用户身份。
int user = model.changeStringToInt(view.getUser());
// 下一步完善changeStringToInt()方法,并写进一步分支流程
}
}
}
完善model类中的changeStringToInt()方法,将用户输入的数据转换为int类型,类型不匹配时返回-1,在Controller中用view.reInput()方法提示用户重新输入。
// Model类中
public int changeStringToInt(String s) {
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
return -1;
}
}
4、判断用户身份
我们通过model.changeStringToInt(view.getUser())获取了用户输入的信息,返回值为0(退出)、1(管理员)、2(普通用户)或其他数字,通过返回值写下一步分支流程,完成主流程控制部分的代码编写。
// Controller类中
public void expressMain() {
while (true) {
view.welcome();
// 将用户输入的数据转化为int类型,并用以判断用户身份。
int user = model.changeStringToInt(view.getUser());
// 用户输入0,打印结束语,通过break跳出循环,结束整个快递程序方法。
if (user == 0) {
view.bye();
break;
}
// 用户为快递管理员,让管理员进一步输入指令,转化为int类型,指引进一步操作。
else if (user == 1) {
// 下一步完善管理员进一步操作的分支方法courierOperation(int num)
courierOperation(model.changeStringToInt(view.getCourierOperation()));
}
// 用户为普通用户,让用户输入取件码,进行取件操作。
else if (user == 2) {
// 稍后完善用户通过取件码取件的方法userOperation(int code)
userOperation(model.changeStringToInt(view.getExpressCode()));
} else {
view.reInput();
}
}
}
5、管理员分支判断
当管理员输入对应的数字后,分支判断管理员不同的增删改查操作。
// Controller类中
public void courierOperation(int courierOperationNum) {
if (courierOperationNum == 1) {
addPress();
}else if (courierOperationNum == 2) {
delPress();
} else if (courierOperationNum == 3) {
updatePress();
} else if (courierOperationNum == 4) {
showAllPress();
} else {
view.reInput();
}
}
6、查找快递
快递员添加快递的时候,需要输入快递单号,但是为了保证存入的快递单号不重复,我们需要在快递员输入单号后先做去重。
具体做法是需要编写一个通过快递单号查找快递对象的方法,后续删除快递、修改快递都需要。因为不属于流程控制,而属于与数据相关的业务代码,固需要写在model类当中。
通过两层for循环来遍历存放快递对象的二维数组,取出每个快递对象后调用它的getExpressNumber()方法获取其快递单号,和输入的快递单号作比较,如果相等,说明通过该单号找到了快递,则返回该快递对象,反之则返回null。
// Model类中
public ExpressModel findExpressByNumber(String expressNumber) {
for (int x=0;x<10;x++) {
for (int y=0;y<10;y++) {
// 需要先判断express[x][y]!=null,否则空对象调用get方法时会报空指针异常
if (express[x][y]!=null && Objects.equals(express[x][y].getExpressNumber(), expressNumber)) {
return express[x][y];
}
}
}
return null;
}
同样的,当用户取件的时候并不知道快递单号,而是通过取件码来查找快递。同时快递员存入快递时系统会随机生成6位取件码,也需要去重功能。所以还需要写一个通过取件码来查找快递的方法,与上方法同理。
// Model类中
public ExpressModel findExpressByCode(int code) {
for (int x=0;x<10;x++) {
for (int y=0;y<10;y++) {
if (express[x][y]!=null && express[x][y].getExpressCode() == code) {
return express[x][y];
}
}
}
return null;
}
最后,管理员选择查看所有快递信息时,并不会手动输入每一个快递单号,而需要两层循环遍历整个数组,需要通过角标获取快递对象的方法。
// Model类中
public ExpressModel getExpressByIndex(int x, int y) {
return express[x][y];
}
我们在Model中可以直接通过express[x][y]获取快递对象,为什么要这样“多此一举”写一个方法返回此对象?因为管理员需要查询所有快递,而Model中的二维数组express[][]是private修饰的。
7、查询所有快递
先判断快递柜内是否有快递,通过两层循环遍历二维数组,当对象不为空时打印出该对象。
为什么不把此方法写在Model中作为一个业务功能,因为在循环的过程中需要调用view视图类中的方法来打印对象,而MVC模式中Model与View是严格剥离的,只能通过Controller粘合在一起,所以此功能应属于流程控制类中的方法。
// Controller类中
public void showAllPress() {
if (model.getSize() == 0) {
view.expressInEmpty();
} else {
// 打印表头
view.printTitle();
// 遍历打印所有快递对象
for (int x = 0; x < 10; x++) {
for (int y = 0; y < 10; y++) {
// 通过getByIndex方法获取x,y下标对应的对象
ExpressModel expressTemp = model.getExpressByIndex(x, y);
// 打印出所有不为空的对象
if (expressTemp != null) {
view.printExpress(expressTemp);
}
}
}
}
}
8、存储快递
a、快递单号去重
管理员存储快递,首先需要输入快递单号,需要有去重功能,而且修改快递信息时,也需要输入快递单号。所以单独写一个通用的让管理员输入不重复的快递单号的方法。
获取管理员输入的单号后,调用通过单号查找快递的方法,如果能查找到快递,说明此单号以存在,提示管理员重新输入,反之说明此单号不存在,直接返回此单号。
// Controller类中
public String getUniqueNumber() {
while (true) {
String expressNumber = view.getExpressNumber();
if (model.findExpressByNumber(expressNumber) != null) {
view.addExpressNumberError();
} else {
return expressNumber;
}
}
}
b、存储快递主流程
跳出此循环方法只能输入正确的不重复的快递单号,而有可能存在管理员忘记单号或点错进入此方法的可能,所以出现上述情况时提示管理员输入0,让方法返回0,在下一步流程控制中对单号是否为0做进一步流程判断。
编写储存快递的流程控制方法,先获取单号,然后提供管理员输入0时跳出方法的出口。如果正常获取单号,需要检查快递柜(二维数组)是否还有空位。如果有空位,需要进一步提示管理员获取快递公司。
有了这些信息后,需要与数据信息对接了,数据业务的代码需要写在model当中,通过model中添加快递的方法,把单号和快递公司的信息传入。
储存好快递后,需要在控制台提示管理员快递存入成功,并显示生成的取件码。下一步完善model.addExpress(String numer, String company)方法。
// Controller类中
public void addPress() {
// 管理员通过getUniqueNumber方法输入要添加的快递单号,确保单号是唯一的
String expressNumber = getUniqueNumber();
// 管理员按错或者忘记单号时输入0,提供跳出方法的出口
if (Objects.equals(expressNumber, "0")) {
return;
}
// 判断快递柜是否已满
if (model.getSize() == 100) {
view.addExpressFull();
} else {
// 调用addExpress方法,将快递单号和快递公司信息传入
ExpressModel expressTemp = model.addExpress(expressNumber, view.getExpressCompany());
// 取出addExpress方法中生成的取件码
int codeTemp = expressTemp.getExpressCode();
view.addExpressOver(codeTemp);
}
}
c、数据修改与存储
当我们调用这个方法传入快递单号和公司的时候,已经确定了二维数组是有空位的,通过对二维数组的两个角标取随机数,获取一个随机的快递抽屉,然后判断当这个抽屉为空的时候,随机生成一个取件码,然后通过有参构造方法创建一个快递对象,存入这个快递抽屉当中,同时需要给快递数量size变量+1。下一步完善随机生成取件码的方法。
// Model类中
public ExpressModel addExpress(String expressNumber, String expressCompany) {
while (true) {
int x = random.nextInt(10);
int y = random.nextInt(10);
if (express[x][y] == null) {
express[x][y] = new ExpressModel(expressNumber, expressCompany, randomCode());
size++;
return express[x][y];
}
}
}
d、随机生成取件码
思路梳理中,我们已经讨论了随机生成100000~999999六位取件码并去重的方法,不再赘述,上代码。
// Model类中
public int randomCode() {
while (true) {
int code = random.nextInt(900000)+100000;
if (findExpressByCode(code) == null) {
return code;
}
}
}
9、删除快递
while循环让管理员输入匹配的快递单号,同样提供无法匹配时跳出循环的出口。同样设计数据业务,需要在Model类中编写方法。通过调用Model类中的delExpress(String expressNumber)方法来删除快递,下一步完善。
// Controller类中
public void delPress() {
while (true) {
// 管理员输入要删除的快递单号
String expressNumber = view.getExpressNumber();
// 当快递柜内无快递或管理员忘记快递单号时,提供跳出循环的出口
if (Objects.equals(expressNumber, "0")) {
break;
}
// 通过delExpress方法来判断该单号是否存在于快递柜中,如果存在则删除快递
if (model.delExpress(expressNumber)) {
view.delExpressOver();
break;
} else {
view.expressNotExist();
}
}
}
这里不在Controller中采用findByNumber()方法来查重,因为这种方法我们查到快递对象后,只能得到方法返回的对象,我们无法通过令对象为null的方法来删除快递,这种方法只是令二维数组这个地址指引的某个具体实例化对象为null,无法从源头上让这个快递抽屉为空。只能通过遍历查找二维数组后,通过角标来定位数组中的位置,用express[x][y] = null来实现删除快递的功能。
// Model类中
public boolean delExpress(String expressNumber) {
for (int x=0;x<10;x++) {
for (int y=0;y<10;y++) {
if (express[x][y]!=null && Objects.equals(express[x][y].getExpressNumber(), expressNumber)) {
express[x][y] = null;
size--;
return true;
}
}
}
return false;
}
10、用户取快递
我们在进入快递系统主流程判断用户身份的时候,如果用户输入的是2(普通用户),则让用户输入取件码,进入userOperation(int code)方法
else if (user == 2) {
userOperation(model.changeStringToInt(view.getExpressCode()));
}
下面完善该流程控制方法,当输入非int类型时,changeStringToInt方法会返回-1,此时进入view.reInput()方法,提示用户输入有误。当用户输入int类型的取件码时,调用Model类中的gerExpress(code)方法来取件,下一步完善该方法。
// Controller类中
public void userOperation(int code) {
if (code == -1) {
view.reInput();
} else {
if (model.getExpress(code)) {
view.getExpressOver();
} else {
view.getExpressFail();
}
}
}
用户取快递和管理员删除快递其实是非常近似的,只不过一个是通过快递单号,一个是通过取件码。既然如此,我们就可以复用Model类中的delExpress(String number)方法来实现用户取快递。
先通过用户输入的取件码,调用findExpressByCode(int code)方法查找快递,如果不存在该取件码对应的快递则返回null对象。如果找到了快递对象,通过get方法取出快递单号,然后调用管理员通过单号删除快递的方法即可。
// Model类中
public boolean getExpress(int expressCode) {
ExpressModel express = findExpressByCode(expressCode);
if (express == null) {
return false;
} else {
return delExpress(express.getExpressNumber());
}
}
11、修改快递
先让管理员输入要修改的快递单号,同样提供跳出循环的出口。然后通过该单号查找快递,如果为空则提示用户核实快递单号,不为空则获取该快递对象,然后调用getUniqueNumber()方法提示管理员重新输入要修改的去重的快递单号,之后输入快递公司,然后再把该对象、修改后的单号和公司信息传给Model类中的updateExpress(express, expressNumber, expressCompany)方法,来修改快递信息。下一步完善该方法。
// Controller类中
public void updatePress() {
while (true) {
// 管理员输入要修改的快递单号
String expressNumberTemp = view.getExpressNumber();
// 当快递柜内无快递或管理员忘记快递单号时,提供跳出循环的出口
if (Objects.equals(expressNumberTemp, "0")) {
break;
}
// 通过findExpressByNumber方法来判断该单号是否存在于快递柜中
ExpressModel express = model.findExpressByNumber(expressNumberTemp);
// 该单号的快递在快递柜中,进一步修改快递信息
if (express != null) {
// 打印修改前的快递信息
view.printTitle();
view.printExpress(express);
// 管理员输入修改后的快递单号
String expressNumber = getUniqueNumber();
if (Objects.equals(expressNumberTemp, "0")) {
break;
}
// 管理员输入修改后的快递公司
String expressCompany = view.getExpressCompany();
model.updateExpress(express, expressNumber, expressCompany);
view.updateExpressOver();
view.printTitle();
view.printExpress(express);
break;
// 该单号的快递不在快递柜中,需要重新进入循环获取正确的单号
} else {
view.expressNotExist();
}
}
}
获得管理员要修改的对象和修改后的单号和公司后,使用set方法来修改快递对象的参数即可
public void updateExpress(ExpressModel express, String expressNumber, String expressCompany) {
express.setExpressNumber(expressNumber);
express.setExpressCompany(expressCompany);
}
至此,快递系统所有功能以完善。
五、完整代码
1、View
package pers.Enrico.ExpressMVC;
import java.util.Scanner;
public class ExpressView {
private final Scanner input = new Scanner(System.in);
public void printTitle() {
System.out.println("快递单号\t快递公司\t取件码");
}
public void printExpress(ExpressModel express) {
System.out.println(express.getExpressNumber()+"\t"+express.getExpressCompany()+"\t"+express.getExpressCode());
}
public void expressInEmpty() {
System.out.println("当前快递柜内无快递。");
}
public void welcome() {
System.out.println("\n===欢迎使用新职课快递柜===");
}
public void bye() {
System.out.println("===欢迎再次使用新职课快递柜===");
}
public String getUser() {
System.out.println("请输入您的身份:1-快递员,2-用户,0-退出");
return input.nextLine();
}
public String getCourierOperation() {
System.out.println("请选择操作:1-存快递 2-删除快递 3-修改快递信息 4-查看所有快递");
return input.nextLine();
}
public String getExpressCode() {
System.out.println("请输入取件码:");
return input.nextLine();
}
public void getExpressOver() {
System.out.println("取件成功!");
}
public void getExpressFail() {
System.out.println("查无此取件码对应的快递,请检查取件码。");
}
public String getExpressNumber() {
System.out.println("请输入快递单号:(输入0返回上一层)");
return input.nextLine();
}
public String getExpressCompany() {
System.out.println("请输入快递公司:");
return input.nextLine();
}
public void addExpressOver(int code) {
System.out.println("快递已成功存入!取件码为:"+code);
}
public void addExpressNumberError() {
System.out.println("该单号的快递已存入,请检查快递单号。");
}
public void addExpressFull() {
System.out.println("快递柜已满,无法存入");
}
public void updateExpressOver() {
System.out.println("快递信息已修改,新信息如下:");
}
public void delExpressOver() {
System.out.println("快递删除成功!");
}
public void expressNotExist() {
System.out.println("该单号的快递不存在,请检查快递单号。");
}
public void reInput() {
System.out.println("您输入的信息有误,请重新输入。");
}
}
2、Model
package pers.Enrico.ExpressMVC;
import java.util.Objects;
import java.util.Random;
public class ExpressModel {
private String expressNumber;
private String expressCompany;
private int expressCode;
private ExpressModel express[][] = new ExpressModel[10][10];
private int size;
Random random = new Random();
public int changeStringToInt(String s) {
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
return -1;
}
}
public ExpressModel findExpressByNumber(String expressNumber) {
for (int x=0;x<10;x++) {
for (int y=0;y<10;y++) {
if (express[x][y]!=null && Objects.equals(express[x][y].getExpressNumber(), expressNumber)) {
return express[x][y];
}
}
}
return null;
}
public ExpressModel findExpressByCode(int code) {
for (int x=0;x<10;x++) {
for (int y=0;y<10;y++) {
if (express[x][y]!=null && express[x][y].getExpressCode() == code) {
return express[x][y];
}
}
}
return null;
}
public ExpressModel getExpressByIndex(int x, int y) {
return express[x][y];
}
public boolean getExpress(int expressCode) {
ExpressModel express = findExpressByCode(expressCode);
if (express == null) {
return false;
} else {
return delExpress(express.getExpressNumber());
}
}
public ExpressModel addExpress(String expressNumber, String expressCompany) {
while (true) {
int x = random.nextInt(10);
int y = random.nextInt(10);
if (express[x][y] == null) {
express[x][y] = new ExpressModel(expressNumber, expressCompany, randomCode());
size++;
return express[x][y];
}
}
}
public int randomCode() {
while (true) {
int code = random.nextInt(900000)+100000;
if (findExpressByCode(code) == null) {
return code;
}
}
}
public boolean delExpress(String expressNumber) {
for (int x=0;x<10;x++) {
for (int y=0;y<10;y++) {
if (express[x][y]!=null && Objects.equals(express[x][y].getExpressNumber(), expressNumber)) {
express[x][y] = null;
size--;
return true;
}
}
}
return false;
}
public void updateExpress(ExpressModel express, String expressNumber, String expressCompany) {
express.setExpressNumber(expressNumber);
express.setExpressCompany(expressCompany);
}
public ExpressModel(String expressNumber, String expressCompany, int expressCode) {
this.expressNumber = expressNumber;
this.expressCompany = expressCompany;
this.expressCode = expressCode;
}
public ExpressModel() {}
public String getExpressNumber() {
return expressNumber;
}
public void setExpressNumber(String expressNumber) {
this.expressNumber = expressNumber;
}
public String getExpressCompany() {
return expressCompany;
}
public void setExpressCompany(String expressCompany) {
this.expressCompany = expressCompany;
}
public int getSize() {
return size;
}
public int getExpressCode() {
return expressCode;
}
public void setExpressCode(int expressCode) {
this.expressCode = expressCode;
}
}
3、Controller
package pers.Enrico.ExpressMVC;
import java.util.Objects;
public class ExpressController {
ExpressView view = new ExpressView();
ExpressModel model = new ExpressModel();
/**
* 快递柜的主程序,包含欢迎、流程控制、和结束语。
*/
public void expressMain() {
while (true) {
view.welcome();
// 将用户输入的数据转化为int类型,并用以判断用户身份。
int user = model.changeStringToInt(view.getUser());
if (user == 0) {
view.bye();
break;
}
// 用户为快递管理员,让管理员进一步输入指令,转化为int类型,指引进一步操作。
else if (user == 1) {
courierOperation(model.changeStringToInt(view.getCourierOperation()));
}
// 用户为普通用户,让用户输入取件码,进行取件操作。
else if (user == 2) {
userOperation(model.changeStringToInt(view.getExpressCode()));
} else {
view.reInput();
}
}
}
/**
* 用户取件的方法
* @param code 让用户输入的取件码
*/
public void userOperation(int code) {
if (code == -1) {
view.reInput();
} else {
if (model.getExpress(code)) {
view.getExpressOver();
} else {
view.getExpressFail();
}
}
}
/**
* 快递管理员执行进一步操作的指令分流方法。
* @param courierOperationNum 管理员输入的操作指令
*/
public void courierOperation(int courierOperationNum) {
if (courierOperationNum == 1) {
addPress();
}else if (courierOperationNum == 2) {
delPress();
} else if (courierOperationNum == 3) {
updatePress();
} else if (courierOperationNum == 4) {
showAllPress();
} else {
view.reInput();
}
}
/**
* 管理员添加快递的方法
*/
public void addPress() {
// 管理员通过getUniqueNumber方法输入要添加的快递单号,确保单号是唯一的
String expressNumber = getUniqueNumber();
if (Objects.equals(expressNumber, "0")) {
return;
}
// 判断快递柜是否已满
if (model.getSize() == 100) {
view.addExpressFull();
} else {
// 调用addExpress方法,将快递单号和快递公司信息传入
ExpressModel expressTemp = model.addExpress(expressNumber, view.getExpressCompany());
// 取出addExpress方法中生成的取件码
int codeTemp = expressTemp.getExpressCode();
view.addExpressOver(codeTemp);
}
}
/**
* 管理员删除快递的方法
*/
public void delPress() {
while (true) {
// 管理员输入要删除的快递单号
String expressNumber = view.getExpressNumber();
// 当快递柜内无快递或管理员忘记快递单号时,提供跳出循环的出口
if (Objects.equals(expressNumber, "0")) {
break;
}
// 通过delExpress方法来判断该单号是否存在于快递柜中
if (model.delExpress(expressNumber)) {
view.delExpressOver();
break;
} else {
view.expressNotExist();
}
}
}
/**
* 管理员修改快递的方法
*/
public void updatePress() {
while (true) {
// 管理员输入要修改的快递单号
String expressNumberTemp = view.getExpressNumber();
// 当快递柜内无快递或管理员忘记快递单号时,提供跳出循环的出口
if (Objects.equals(expressNumberTemp, "0")) {
break;
}
// 通过findExpressByNumber方法来判断该单号是否存在于快递柜中
ExpressModel express = model.findExpressByNumber(expressNumberTemp);
// 该单号的快递在快递柜中,进一步修改快递信息
if (express != null) {
// 打印修改前的快递信息
view.printTitle();
view.printExpress(express);
// 管理员输入修改后的快递单号
String expressNumber = getUniqueNumber();
if (Objects.equals(expressNumberTemp, "0")) {
break;
}
// 管理员输入修改后的快递公司
String expressCompany = view.getExpressCompany();
model.updateExpress(express, expressNumber, expressCompany);
view.updateExpressOver();
view.printTitle();
view.printExpress(express);
break;
// 该单号的快递不在快递柜中,需要重新进入循环获取正确的单号
} else {
view.expressNotExist();
}
}
}
/**
* 管理员查看所有快递的方法
*/
public void showAllPress() {
if (model.getSize() == 0) {
view.expressInEmpty();
} else {
// 打印表头
view.printTitle();
// 遍历打印所有快递对象
for (int x = 0; x < 10; x++) {
for (int y = 0; y < 10; y++) {
// 通过findByIndex方法获取x,y下标对应的对象
ExpressModel expressTemp = model.getExpressByIndex(x, y);
// 打印出所有不为空的对象
if (expressTemp != null) {
view.printExpress(expressTemp);
}
}
}
}
}
/**
* 让管理员输入不重复的快递单号
* @return 返回唯一的不重复的快递单号
*/
public String getUniqueNumber() {
while (true) {
String expressNumber = view.getExpressNumber();
if (model.findExpressByNumber(expressNumber) != null) {
view.addExpressNumberError();
} else {
return expressNumber;
}
}
}
}
4、Test
package pers.Enrico.ExpressMVC;
public class ExpressTest {
public static void main(String[] args) {
ExpressController controller = new ExpressController();
controller.expressMain();
}
}
六、测试代码
