北航计算机学院面向对象(2023 第四单元)
简介
本文将以笔者学习过程中的思考感悟为基础,对2023北航计算机学院面向对象课程第四单元(作业13~15)的架构搭建和程序设计思路做简明的描述;如有不同见解,欢迎学习交流。
正向建模与开发
本单元可以说是面向对象课程对架构设计考验最严格的一次(之前的三次作业都有固定的架构模板或者根本不需要设计架构)。
在实际的开发问题中,问题的逻辑往往一开始是模糊隐蔽且错综复杂的,需要对问题从现实维度进行理解,将问题的逻辑链和事件链抽离出来进行建模,服务与后续架构设计。
本节将基于本单元的编程实践讨论正向建模开发的过程。
一次微型重构
在第一次作业公布后,给我的最直接的感觉是书的传递规则十分复杂,在那个周二的晚上,我做出了第一版设计,这个设计虽然在后续几天被改得面目全非,但其中一次改动使我对这个单元正向建模的风格(或者完成作业的技巧?)有了一个大概的把握。
图书管理系统,本质上是书籍在不同对象间的规则传递,那么需要为书建立实例化对象吗,或是在各个对象内仅对书进行分类计数?,弄清这个问题是本单元建模和开发的基础:
- 在第一版设计中,我没有为书籍建立专门的类,而是在各个对象内对书籍进行分类计数;书籍的传递也只是简单的数量加减。
- 在经过第二天一个上午的思考后,我决定改动这个设计,为书籍建立相应的类,在拥有书籍的对象中建立容器管理书籍;书籍的传递涉及对象在容器中的出入。
为什么做这样的改动呢,看上去第一版似乎更加简单且易于实现?
其实是这样的:第一版设计确实可以完成本单元第一次作业,但这样做的风险很大,因为现实中书籍是有很多维度的性质的,即使完全重名的两本书,也可能有不同的状态,实例化书籍对象在模型构建和后续迭代的过程中都使代码更加具象且易于管理。
因此,我理解的正向建模实际上就是使用计算机语言对开发场景下现实世界中的物理对象的实体和行为进行最大贴合的建模(这样做的性能可能不是最佳,但模型的可读性强,性能可以在代码构建后进行局部优化)。
逻辑链的抽离
开发场景下,物理世界实体的行为逻辑往往是十分复杂的,如何剖析入理,明白代码需要遵循哪些场景逻辑是正向建模中十分重要的一步。
在本单元作业中,图书馆管理管理系统主要涉及以下几条逻辑链:
- 借阅书籍(学生——图书馆)
- 预订书籍(学生——图书馆)
- 运输书籍(图书馆——图书馆)
- 购入书籍(图书馆)
- 归还书籍(学生——图书馆)
- 馆内分流(图书馆)
在这些逻辑链中,往往存在行为触发规则和行为进行所需要的信息。对于这两类关键控制因素:
* 行为触发规则
在各方法的代码实现中体现,多体现为对目标对象状态和自身状态的检测。
* 行为依赖信息
在各个对象实体中用一定的数据结构进行保存,行为通过检索数据进行执行,执行结果有时也会对其依赖的数据产生影响。
架构设计和UML图
本单元的架构设计,我始终遵循一个认知:
所有行为对象都是书籍的集合!
架构的搭建
在本单元作业的要求中,我们可以发现,所有的行为实体(学生、书架、管理员……)都具有一些共同的功能,例如:
- 给出一本书
- 接纳一本书
- 书籍的成批发出和接受
但各个实体也有自己独特的功能方法,例如:
- 学生可以损坏或丢失书籍
- 预订管理员可以记录申请
- 后期处可以修复书籍
这些特征暗示了各个行为对象可以有共同的方法又保持自身一定的独特性,即可以使用继承关系继承一个“书集合”的父类。
但我在架构设计的时候为了避免继承时子类需要实现过多的额外方法,直接在 BookGroup 类中实现了尽可能多的方法(可能不被全部行为实体共有),后在各个行为实体中直接实例化该类,间接调用方法。
以本单元第一次作业为例可以形象地感受这个设计思路:
可以发现:
- bookGroup作为通用类被几个主要行为对象实例化
- 行为对象间的交互在 MainClass 中发生
- 为学生的申请单独开辟 Requirement 类记录格式化的信息
- 使用 DateOpt 类进行日期转换和计算
这种架构在视觉上十分清晰简洁,对象间的交互统一管理,减少了不必要的参数传递。
架构的迭代
本单元迭代工作量较大的一次是第一次到第二次作业的迭代;在本次迭代中由于引入了校际借阅和购入图书等新行为,使得逻辑链更加交错复杂;不过得益于书籍的实例化和通用方法的使用,本次迭代的工作量相比之下没有失控,具体架构如下:
可以发现有以下变化:
- 封装原本的校内交互为 School 类的方法
- 在 MainClass 中实例化多个 school,处理校际交互行为
- 构建运输类专门处理校际书籍的交接
- 图书管理员和采购部分离各司其职
本架构在最大化维持原本的布局的情况下完成了复杂功能的扩展,可见原本的架构设计是较为合理的。
架构设计思维的演进
在面向对象的课程中,虽然老师和助教一直强调架构设计的重要性,但十分惭愧地说:我在最后一单元作业中才真正自主从头到尾设计出一套架构:
- 第一单元的表达式解析
- 使用经典的递归下降方法(任务练习作业中已经打好框架)
- 使用预处理进行简化,方便后续操作
- 各部分功能类相互分离,降低耦合
- 第二单元多线程电梯模拟
- 参考学长的博客,采用自由竞争的策略,架构较为简单
- 主要分为电梯运行部分和开关门动作单独处理部分
- 由于没有控制器的加入,各部分独立性强,但性能不顶尖
- 第三单元 JML 撰写和编程
- 几乎没考虑架构,全部完形填空去了(×
- 回看第三单元的作业,实现的是人群间通讯模拟,对性能要求高
- 使用实现与设计分离,优化算法提升性能
- 第四单元图书管理系统
- 实例化书本带来了架构的可靠性和可维护性
- 公共类的引入带来了架构的可扩展性和简洁性
- 低耦合度的方法使新功能的实现更加便携
测试思维的演进
在面向对象课程中,测试思维的好坏往往能决定程序的品质和分数,在四个单元的磨炼中,我也形成了一套测试思维:
- 第一单元初接触较为庞大的程序,测试抓不住重点,采用随机数据生成的方式进行随机测试,这种测试效率不高且易发生疏漏。
- 第二单元中,多线程的引入使对拍和debug不再方便,采用边界条件构造和大数据量测试的方法,集中测试程序在极端情况下的可靠性,效果较为出色。
- 第三单元,使用静态分析、OKtest测试和对拍程序相结合的方式,在本单元完成了一个数据生成和自动对拍器,本单元对性能的强调也是突出的,故有针对地构造大量耗时数据十分重要。
- 第四单元,测试被淡化,但绝不是不重要,由于充分的考量和架构设计,我们的程序具有较高的起点,犯错概率大大降低;本单元采用对逻辑链依次构造数据的方式,测评程序完备性。
课程总结与收获
在 2023北航面向对象 的课程中,我在java基本程序设计、复杂架构搭建、测试策略和程序编写、形式化验证、程序结构建模等方面收获良多,虽然最后取得的成绩并不顶尖,但这些面向对象的思维观念和良好的编程习惯已经牢牢地刻印在脑海中。
这门课给我的最直观感受是要求严格但知识密度极大,是教授真正本领的一门课程,我很荣幸能全程参与。
最后,希望北航面向对象课程越办越好。