一、任务概述
本练习用于熟悉快递管理业务,完成快递管理控制台项目,具体需求如下:
采用二维数组的方式存储快递
- 管理员
- 录入快递
- 柜子位置(系统产生,不能重复)
- 快递单号(输入)
- 快递公司(输入)
- 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()