目标:编写具有可复用性和可维护性的软件,主要使用以下软件构造技术:
子类型、泛型、多态、重写、重载
继承、代理、组合
常见的OO设计模式
语法驱动的编程、 正则表达式
基于状态的编程
API设计、API复用
- 实验环境配置
环境配置无问题。
在这里给出你的GitHub Lab3仓库的URL地址(Lab3-学号)。
https://github.com/ComputerScienceHIT/HIT-Lab3-2021112648
请仔细对照实验手册,针对每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
首先请列出你要完成的具体应用场景(至少3个,1和2中选一,3必选,4和5中选一,鼓励完成更多的应用场景)。
- TrackGame
- AtomStructure
- SocialNetworkCircle
分析你所选定的多个应用场景的异同,理解需求:它们在哪些方面有共性、哪些方面有差异。
首先,他们都是多轨道结构,它们的轨道十分类似,都可以看成一个只有名字和半径属性的圆。它们的轨道物体也有一些相似之处:比如都要求有名字。他们都要求有对于轨道、轨道上的物体的一系列增删改查的操作的要求。
不同之处:
有的要求各个物体之间有有向或无向关系,有的各个物体之间没有区别比如电子,有的有区别比如运动员。
使用的正则表达式如下:
使用Matcher进行捕获
-
- 面向复用的设计:CircularOrbit<L,E>
定义数据结构如下
其中OrbitMap存放Track和E的对应关系,一个Track对应一个处于这个Track上的E链表。
centralRelationMap存放中心物体的关系,map为<和中心物体有关系的物体,权重>
trackRelationMap存放物体和这个轨道有关系的物体的map,
map为<轨道物体,<与这个轨道物体有关系的物体,权重>>
方法: | 功能: |
setCentralObject(L centralObject); | 设置中心物体 |
getCentralObject(); | 返回中心物体 |
addTrack(Track t); | 增加一条轨道 |
removeTrack(Track t); | 删除一条轨道 |
getTrackNum(); | 返回轨道数目 |
getObjectNumonTrack(Track t); | 统计一条轨道有多少物体 |
addObjectToTrack(Track t, E object); | 向轨道上增加物体 |
removeObjectOnTrack(Track t, E object); | 删去一个轨道上的某个物体 |
addtrackRelation(E object1, E object2, double distance) | 在两个轨道物体之间新增关系 |
addcentralRelation(E object, double distance); | 在轨道物体和中心物体之间新增关系 |
getObjectDistributionEntropy(); | 计算轨道系统的信息熵 |
getLogicalDistance(E e1, E e2); | 获得两物体间逻辑距离 |
getDifference(CircularOrbit<L, E> c); | 比较当前orbit和目标orbit的不同 return 一个different对象,记录两个orbit的区别 |
getSortedTracks(); | 获得当前orbit包含的所有轨道按半径排列成的链表 |
drawpicture(); | 可视化方法 |
contains(E e); | 判断当前Orbit是否包含某个元素e |
getObjectTrack(E e); | 返回某个元素e所在的Object对象 |
getCentralConnectedObject(); | 返回与中心连接的物体构成的集合 |
getTrackConnectedObject(E object) | 返回与某个轨道物体连接的所有物体 |
checkRep(){ | 检查合法性 |
-
- 面向复用的设计:Track
有两个域 name名字 和radius半径,实现Comparable依靠半径排序。
方法 | 功能 |
Track(String name, double radius) | 构造方法,传入名字,半径 |
checkRep() { | 检查名字非空,半径大于0 |
int compareTo(Track o) { | 重写Comparable接口的compareTo方法,依据轨道半径对轨道进行大小比较,进而排序 |
只有一个域名字,方法:
Name的getter方法和CentralObject(String name) 构造方法
以及checkRep() 方法检查名字非空。
-
- 面向复用的设计:PhysicalObject
只有一个域名字,方法:
Name的getter方法和PhysicalObject (String name) 构造方法
以及checkRep() 方法检查名字非空。
-
- 可复用API设计
很简单,直接调用ConcreteCircularOrbit中的对应方法。
全部设置成static方法可以不用实例化CircularOrbitAPIs就使用这些方法。
-
- 图的可视化:第三方API的复用
具体实现位于ConcreteCircularOrbit:
按轨道依次计算点的位置利用drawOval方法画圆,fillOval方法填色,drawString方法写名字。
测试效果:
在每个具体Object类中实现一个静态的getInstance方法,不需要实例化Athlete即可调用
-
-
- Iterator设计模式
-
设计OrbitIterator类实现Iterator接口。
ObjectList是保存所有物体的list,每次取next从ObjectList取值。
Index是迭代指针。
方法 | 功能 |
OrbitIterator(Map<Track, List<E>> orbitMap) | 构造函数,根据关系Map orbitMap构造一个ObjectList作为迭代的输出 |
hasNext() | 判断迭代指针是否等于size,如果是则没有下一个了 |
next() | 取元素,Index++ |
-
- 应用设计与开发
利用上述设计和实现的ADT,实现手册里要求的各项功能。
以下各小节,只需保留和完成你所选定的应用即可。
从starter下的startMain.java运行
显示如下菜单:
输入1,2,3进入功能
目录如下
Athlete类继承自PhysicalObject作为轨道物体
TrackCircularOrbit类继承自ConcreteCircularOrbit作为具体轨道结构
重写了toString方法输出多轨道结构
重写了checkRep()方法按要求检查轨道特性
TrackCircularOrbitBuilder类继承自CircularOrbitBuilder
主要是重写了createCircularOrbit方法,生成TrackCircularOrbit。
方法:
方法 | 功能 |
GameMenu() { | 菜单 |
gameMain | 功能主体,读取文件,构造每个TrackCircularOrbit,实现菜单中的每个功能。 |
arrangeOrbit(Strategy strategy) | 策略安排方法,根据athleteList和trackNum和构造策略,生成构造方案 |
其中:
Particle类继承自PhysicalObject作为轨道物体
getElectron返回一个没有区别的电子,
getNucleus返回一个有名字区别的原子核
Memento类和TransitCareTaker用来实现备忘录模式状态恢复功能,上面介绍过。
AtomCircularOrbit类继承自ConcreteCircularOrbit作为具体轨道结构
主要是重写了toString方法。新增方法如下:
方法 | 功能 |
transit(Track t1, Track t2) { | 电子跃迁方法 |
removeElectron(Track t) | 从某条轨道删去一个电子,因为电子互相之间没有区别,所以只需一个轨道参数。 |
重写toString | 输出轨道结构 |
AtomCircularOrbitBuilder类继承自CircularOrbitBuilder
主要是重写了createCircularOrbit方法,生成AtomCircularOrbit。
Person类继承自PhysicalObject作为轨道物体
relationKeeper类和是一个用来保存文件读入的类,因为读取文件读入的过程没法将读取到的人名 马上与实例对应起来,所以构造relationKeeper保存每个关系的人名string。
SocialNetCircularOrbit类继承自ConcreteCircularOrbit作为具体轨道结构
新增方法如下:
方法 | 功能 |
reArrange | 重整关系网络 |
重写drawpicture方法 | 实现可视化,这个轨道结构要求可视化关系,所以加上画边的功能。 |
重写toString | 输出轨道结构 |
SocialNetCircularOrbitBuilder类继承自CircularOrbitBuilder
主要是重写了createCircularOrbit方法,生成SocialNetCircularOrbit。
新增bulidRelations,从读取的文件输入构造人际关系图,随后通过reArrange构造轨道关系图。
以下各小节,只需保留和完成你所选定的应用即可。
由于使用了strategy模式,
只需要新建一个strategy:Relay4Strategy继承自Strategy接口实现一次分配四个人分配就行了
修改AtomStructure的声明
增加addCentralObject方法
修改AtomCircularOrbitBuilder中的bulidPhysicalObjects方法,在构造中心物体时传入一个集合。
在文件读入时也改成读取中心物体,构造集合之后build。
增加Particle类的工厂方法使其能生成质子和中子
修改toString方法,输出原子核的具体情况。
这里只需要将改为只增加一个有向关系即可。
请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。
每次结束编程时,请向该表格中增加一行。不要事后胡乱填写。
不要嫌烦,该表格可帮助你汇总你在每个任务上付出的时间和精力,发现自己不擅长的任务,后续有意识的弥补。
日期 | 时间段 | 计划任务 | 实际完成情况 |
4.15 | 14-22 | 翻看实验手册,补充知识 | 完成 |
4.16 | 14-22 | 建立项目,设计文件结构 | 完成 |
4.17 | 18-23 | 完成3.4 | 完成 |
4.19 | 15-23 | 完成3.5 3.6 3.7 | 完成 |
4.21 | 15-23 | 完成3.8 | 未完成 |
4.22 | 19-22 | 3.8 | 完成 |
4.25 | 18-22 | 3.9 | 完成 |
4.28 | 18-22 | 3.10 | 未完成 |
4.29 | 18-22 | 3.10 | 完成 |
5.1 | 8-22 | App1 | 未完成 |
5.2 | 8-22 | App1,App2 | 完成app1 |
5.3 | 8-22 | App2,app3 | 完成app2 |
5.4 | 8-22 | App3,补充test | 完成app3 |
5.5 | 8-22 | 撰写实验报告 | 完成 |
遇到的难点 | 解决途径 |
正则表达式不熟练 | 上网学习 |
Swing库不会用 | 上网学习 |
可复用性考虑不足。
- 重新思考Lab2中的问题:面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?本实验设计的ADT在五个不同的应用场景下使用,你是否体会到复用的好处?
- 重新思考Lab2中的问题:为ADT撰写复杂的specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?你是否愿意在以后的编程中坚持这么做?
- 之前你将别人提供的API用于自己的程序开发中,本次实验你尝试着开发给别人使用的API,是否能够体会到其中的难处和乐趣?
- 在编程中使用设计模式,增加了很多类,但在复用和可维护性方面带来了收益。你如何看待设计模式?
- 你之前在使用其他软件时,应该体会过输入各种命令向系统发出指令。本次实验你开发了一个解析器,使用语法和正则表达式去解析输入文件并据此构造对象。你对语法驱动编程有何感受?
- Lab1和Lab2的大部分工作都不是从0开始,而是基于他人给出的设计方案和初始代码。本次实验是你完全从0开始进行ADT的设计并用OOP实现,经过三周之后,你感觉“设计ADT”的难度主要体现在哪些地方?你是如何克服的?
- 你在完成本实验时,是否有参考Lab4和Lab5的实验手册?若有,你如何在本次实验中同时去考虑后续两个实验的要求的?
- 关于本实验的工作量、难度、deadline。
- 到目前为止你对《软件构造》课程的评价。
1. 复用极大减小了工作量,但也增加了思考量。
2. 时刻提醒自己注意自己的设计,注意防止泄露
3. 使用别人的API画图很方便很开心。感觉用几行代码就能做很牛逼的事情。
4. 合理运用设计模式十分有许多好处,针对不同的ADT使用恰当的设计模式很重要
5. 避免了自己输入的繁琐过程。可以很方便快捷得处理海量数据。
6. 对物体的抽象
7. 参考了,但没有想法,还是先跟着课程学再说了
8. 难度中等但是工作量真的很大
9. 工作量很大。要求繁多。