本人所写的代码已经上传至GitHub。仓库地址:https://github.com/DeepChirp/BUAA_2024_OOP_ACP

前段时间把面向对象程序设计的迭代作业写完了,感觉在此过程中学到了不少,所以想写一篇文章来总结一下自己的学习经历。

作业要求

作业要求是实现教务云平台。迭代分为两次,第一次是完成系统基本框架搭建,第二次则是新增和完善功能。

两次迭代总共要求实现的功能如下:

命令符说明
quit关机
register用户注册
login用户登录
logout用户登出
printInfo打印用户信息
createCourse创建课程
listCourse查看课程
selectCourse选择课程
cancelCourse注销课程
switch切换用户
inputCourseBatch批量导入课程
outputCourseBatch批量导出课程
listStudent查看选课学生
removeStudent移除选课学生
listCourseSchedule查看课表

设计思路

第一次迭代的时候,确实想着能够AC就行,于是使用面向过程的方法,把所有的功能全部都实现了。

1
2
3
4
5
6
7
8
9
10
11
12
13
src/
├── Test.java
├── commands/
│ ├── CommandHandler.java
│ └── UserRegistry.java
├── services/
│ ├── Course.java
│ └── UserService.java
└── user/
├── Administrator.java
├── Student.java
├── Teacher.java
└── User.java

写代码的时间确实不长,同时行数也比多数人的短不少,大约有700行。不过从上面的结构就能看出来,代码的质量确实不高。几乎所有的操作都在UserService类中实现,包括用户的注册、登录、登出等操作,以及课程的相关操作。而UserRegistry类中存放了操作用户的方法,导致UserService类还要调用UserRegistry类中的方法。仅有User类完成了封装。为了实现方便,所有的变量和方法都是public(即使一个方法是public的,但是只要不调用它,那么它就是private的)

后来想着,仅仅只是AC是不够的,还要考虑代码的可读性、可维护性。所以在第二次迭代的时候,我就重构了代码。先是拆分UserService类中的方法,将每一条命令分别放在不同的类中实现,并且封装了用户和课程相关的方法,调整了访问权限。然后是统一了异常处理,先前如果出现异常,处理的方式还很混乱:有的是直接打印异常,有的是直接返回字符串,让调用者自己处理;现在则是将异常抛给上层,由上层处理。最后还编写了少量的测试代码,以便检验关键方法的正确性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
src/
├── Test.java
├── commands/
│ └── CommandHandler.java
├── course/
│ └── Course.java
├── except/
│ ├── CancelCourseException.java
│ ├── CreateCourseException.java
│ ├── LoginException.java
│ ├── LogoutException.java
│ ├── PrintInfoException.java
│ ├── RegistrationException.java
│ ├── SelectCourseException.java
│ └── UserListCourseScheduleException.java
├── services/
│ ├── CourseCancel.java
│ ├── CourseCreate.java
│ ├── CourseList.java
│ ├── CourseSelect.java
│ ├── CourseService.java
│ ├── UserLogin.java
│ ├── UserLogout.java
│ ├── UserPrintInfo.java
│ ├── UserRegistry.java
│ └── UserService.java
├── user/
│ ├── Administrator.java
│ ├── Student.java
│ ├── Teacher.java
│ └── User.java
└── utils/
└── ValidationUtils.java
test/
├── commands/
│ └── CommandHandlerTest.java
└── utils/
└── ValidationUtilsTest.java

重构后,代码的行数增加了不少,总共约有1000行,但是代码的质量却有了很大的提升。重构的时间比写代码的时间还要长了不少,毕竟先前写的代码太烂了,而重构后考虑的东西就更多了。

在此基础上,添加新功能也变得很容易,只需要在services包中添加新类,然后调用已经封装好的方法判断,最终在CommandHandler类中添加对应的处理。最终的结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
src/
├── Test.java
├── commands/
│ └── CommandHandler.java
├── course/
│ └── Course.java
├── except/
│ ├── CancelCourseException.java
│ ├── CourseBatchException.java
│ ├── CourseListStudentException.java
│ ├── CourseRemoveStudentException.java
│ ├── CreateCourseException.java
│ ├── ListCourseException.java
│ ├── LoginException.java
│ ├── LogoutException.java
│ ├── PrintInfoException.java
│ ├── RegistrationException.java
│ ├── SelectCourseException.java
│ ├── SwitchUserException.java
│ └── UserListCourseScheduleException.java
├── services/
│ ├── CourseBatchService.java
│ ├── CourseCancel.java
│ ├── CourseCreate.java
│ ├── CourseList.java
│ ├── CourseListStudent.java
│ ├── CourseRemoveStudent.java
│ ├── CourseSelect.java
│ ├── CourseService.java
│ ├── UserListCourseSchedule.java
│ ├── UserLogin.java
│ ├── UserLogout.java
│ ├── UserPrintInfo.java
│ ├── UserRegistry.java
│ ├── UserService.java
│ └── UserSwitch.java
├── user/
│ ├── Administrator.java
│ ├── Student.java
│ ├── Teacher.java
│ └── User.java
└── utils/
├── ObjectStreamUtil.java
└── ValidationUtils.java
test/
├── commands/
│ └── CommandHandlerTest.java
└── utils/
└── ValidationUtilsTest.java

不过,设计应该还能更好。目前设计最主要的问题是对于类的拆分是基于命令的,而不是基于功能的。此外,有些类之间的耦合度可能还是比较高,比如CourseService类中的方法,有的是对课程的操作,有的是对学生的操作。这些问题暂时还没有想到很好的解决方案。

思考

个人认为「面向对象」和「面向过程」两种编程范式并不是非此即彼的关系,而是相互结合、相辅相成的。对于程序而言,肯定会有执行的过程,也会有执行的对象。所以在「面向对象」时,也会考虑对象执行的过程;而在「面向过程」时,也会考虑过程中的对象。而这两种编程范式,只是强调了不同的方面。

并且,编程语言和编程范式并不是强关联的。Java是面向对象的,但只是提供了面向对象的特性,而并没有限制不能使用面向过程的方法。个人认为,Java中的方法也是面向过程的,只是在Java中,方法是属于类的,所以才会说Java是面向对象的。