前段时间把面向对象程序设计的迭代作业写完了,感觉在此过程中学到了不少,所以想写一篇文章来总结一下自己的学习经历。
作业要求
作业要求是实现教务云平台。迭代分为两次,第一次是完成系统基本框架搭建,第二次则是新增和完善功能。
两次迭代总共要求实现的功能如下:
| 命令符 | 说明 |
|---|
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是面向对象的。