目标:编写具有可复用性和可维护性的软件,主要使用以下软件构造技术:
子类型、泛型、多态、重写、重载
继承、代理、组合
常见的OO设计模式
语法驱动的编程、 正则表达式
基于状态的编程
API设计、API复用
环境配置没有遇到问题。
在这里给出你的GitHub Lab3仓库的URL地址(Lab3-学号)。
https://github.com/ComputerScienceHIT/Lab3-1170300817
首先请列出你要完成的具体应用场景(至少3个,1和2中选一,3必选,4和5中选一,鼓励完成更多的应用场景)。
分析你所选定的多个应用场景的异同,理解需求:它们在哪些方面有共性、哪些方面有差异。
首先,他们都是多轨道结构,它们的轨道十分类似,都可以看成一个只有名字和半径属性的圆。它们的轨道物体也有一些相似之处:比如都要求有名字。他们都要求有对于轨道、轨道上的物体的一系列增删改查的操作的要求。
不同之处:
有的要求各个物体之间有有向或无向关系,有的各个物体之间没有区别比如电子,有的有区别比如运动员。
使用的正则表达式如下:
设计好正则表达式后,用它构造Pattern,Matcher实现捕获
定义的数据结构如下
其中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 |
比较当前orbit和目标orbit的不同 return 一个different对象,记录两个orbit的区别 |
getSortedTracks(); |
获得当前orbit包含的所有轨道按半径排列成的链表 |
drawpicture(); |
可视化方法 |
contains(E e); |
判断当前Orbit是否包含某个元素e |
getObjectTrack(E e); |
返回某个元素e所在的Object对象 |
getCentralConnectedObject(); |
返回与中心连接的物体构成的集合 |
getTrackConnectedObject(E object) |
返回与某个轨道物体连接的所有物体 |
checkRep(){ |
检查合法性 |
有两个域 name名字 和radius半径,实现Comparable依靠半径排序。
以下省略getter setter方法
方法 |
功能 |
Track(String name, double radius) |
构造方法,传入名字,半径 |
checkRep() { |
检查名字非空,半径大于0 |
int compareTo(Track o) { |
重写Comparable接口的compareTo方法,依据轨道半径对轨道进行大小比较,进而排序 |
只有一个域名字,方法:
Name的getter方法和CentralObject(String name) 构造方法
以及checkRep() 方法检查名字非空。
只有一个域名字,方法:
Name的getter方法和PhysicalObject (String name) 构造方法
以及checkRep() 方法检查名字非空。
很简单,直接调用ConcreteCircularOrbit中的对应方法。
有一个getPhysicalDistance方法没有实现,因为选择的是应用135对物体的物理位置都没有要求。
全部设置成static方法可以不用实例化CircularOrbitAPIs就使用这些方法。
具体实现位于ConcreteCircularOrbit:
主要是构造一个JPanel,重写paint方法:
按轨道依次计算点的位置利用drawOval方法画圆,fillOval方法填色,drawString方法写名字。
测试效果:
在每个具体Object类中实现一个静态的getInstance方法,不需要实例化Athlete即可调用
设计抽象CircularOrbitBuilder类作为具体builder类的父类
包含一个concreteCircularOrbit就是正在build过程中的Orbit
方法如下:
方法 |
功能 |
getConcreteCircularOrbit() { |
返回构造完成的对象 |
abstract void createCircularOrbit(); |
抽象方法,实现时创建具体类型的Orbit |
bulidPhysicalObjects(L centralObj, Map |
根据传入的centralObj和ObjectMap初始化concreteCircularOrbit中的关系Map |
bulidTracks(List |
根据传入的trackList构造concreteCircularOrbit |
之后再根据具体应用实现子builder类:
具体调用举例:
先初始化具体Orbit类,然后依次传入轨道列表,关系Map。最后完成构造输出。
设计OrbitIterator类实现Iterator接口。
ObjectList是保存所有物体的list,每次取next从ObjectList取值。
Index是迭代指针。
方法如下:
方法 |
功能 |
OrbitIterator(Map |
构造函数,根据关系Map orbitMap构造一个ObjectList作为迭代的输出 |
hasNext() |
判断迭代指针是否等于size,如果是则没有下一个了 |
next() |
取元素,Index++ |
如图:
在TrackGame中设计一个strategy接口和两个具体实现的strategy类,分别能执行随机排序和按成绩排序。
只有一个方法
接收传入的运动员列表和赛道列表,返回构造好的关系Map。用于生成TrackCircularOrbit。
调用举例:
之后根据返回的安排好的关系Map。生成TrackCircularOrbit。
实现一个抽象ADT Memento
记录每次跃迁的起止轨道。
再实现一个TransitCareTaker类
其中实现一个Memento的栈结构,契合恢复跃迁时先进先出的操作特点。
使用举例:
跃迁:
恢复:
利用上述设计和实现的ADT,实现手册里要求的各项功能。
以下各小节,只需保留和完成你所选定的应用即可。
从starter下的startMain.java运行
显示如下菜单:
输入1,2,3进入功能
目录如下
Athlete类继承自PhysicalObject作为轨道物体,具有以下新增属性
TrackCircularOrbit类继承自ConcreteCircularOrbit作为具体轨道结构
重写了toString方法输出多轨道结构
重写了checkRep()方法按要求检查轨道特性
TrackCircularOrbitBuilder类继承自CircularOrbitBuilder
主要是重写了createCircularOrbit方法,生成TrackCircularOrbit。
TrackGame类:具有下列域:
GameType轨道类型,trackNum轨道数量,athleteList运动员列表
trackOrbitList是一个TrackCircularOrbit的list结构,因为每次安排比赛生成多个轨道结构,用这个list存放生成的所有TrackCircularOrbit。
trackBuilder是TrackCircularOrbitBuilder的一个实例。用来build每一个TrackCircularOrbit对象。
方法:
方法 |
功能 |
GameMenu() { |
菜单 |
gameMain |
功能主体,读取文件,构造每个TrackCircularOrbit,实现菜单中的每个功能。 |
arrangeOrbit(Strategy strategy) |
策略安排方法,根据athleteList和trackNum和构造策略,生成构造方案 |
运行TrackGame.java文件出现菜单:
读取文件:
随机分配赛道:
按成绩分配赛道:
增加一条赛道:
重新分配之后,可见轨道确实多了一条
增加运动员:
可以看到,按成绩重新安排后出现了该选手,并且最快出场:
删除选手:
再次重新安排方案可见最快的选手aaaa已经移除:
删除轨道:
可见赛道变为了四条
重新可视化:
交换两名选手:
交换10号和9号:也就是按成绩排的头两名
交换前:
交换后:
计算信息熵:
目录结构如下
其中:
Particle类继承自PhysicalObject作为轨道物体,具有以下新方法:
getElectron返回一个没有区别的电子,
getNucleus返回一个有名字区别的原子核
Memento类和TransitCareTaker用来实现备忘录模式状态恢复功能,上面介绍过。
AtomCircularOrbit类继承自ConcreteCircularOrbit作为具体轨道结构
主要是重写了toString方法。新增方法如下:
方法 |
功能 |
transit(Track t1, Track t2) { |
电子跃迁方法 |
removeElectron(Track t) |
从某条轨道删去一个电子,因为电子互相之间没有区别,所以只需一个轨道参数。 |
重写toString |
输出轨道结构 |
AtomCircularOrbitBuilder类继承自CircularOrbitBuilder
主要是重写了createCircularOrbit方法,生成AtomCircularOrbit。
AtomGame类:具有下列域:
trackNum轨道数量,Nucleus原子核
transitCareTaker是TransitCareTaker的一个实例。用来实现备忘录模式,具体是把每次跃迁记录成Memento保存下来,恢复时弹出上一个Memento。
atomCircularOrbit就是当前使用的轨道系统实例。
atomOrbitBuilder是AtomCircularOrbitBuilder的一个实例。用来build每一个AtomCircularOrbitBuilder对象。
功能演示:
开始:读取文件并显示系统结构:
跃迁功能:
回退功能:
可视化:
增加新轨道:因为原子结构多条轨道没有区别,所以默认在外侧加一条:生成track5
画图也可见外圈没有电子的新轨道:
删除轨道:
需要输入轨道序号:
可见轨道2不见了
增加电子:因为电子之间互相没有什么不同,所以只需要指定增加电子的轨道:
计算信息熵:
目录结构:
其中
其中:
Person类继承自PhysicalObject作为轨道物体,具有以下新域:
Age保存年龄,gender是性别
提供一个静态工厂方法
relationKeeper类和是一个用来保存文件读入的类,因为读取文件读入的过程没法将读取到的人名 马上与实例对应起来,所以构造relationKeeper保存每个关系的人名string。
SocialNetCircularOrbit类继承自ConcreteCircularOrbit作为具体轨道结构
新增方法如下:
方法 |
功能 |
reArrange |
重整关系网络 |
重写drawpicture方法 |
实现可视化,这个轨道结构要求可视化关系,所以加上画边的功能。 |
重写toString |
输出轨道结构 |
SocialNetCircularOrbitBuilder类继承自CircularOrbitBuilder
主要是重写了createCircularOrbit方法,生成SocialNetCircularOrbit。
新增bulidRelations,从读取的文件输入构造人际关系图,随后通过reArrange构造轨道关系图。
SocialNetworkGame类:具有下列域:
personList人列表,centralUser中心物体 ,keeperList是一个关系读取寄存器
socialCircularOrbit是当前操作的SocialNetCircularOrbit对象。
socialCircularOrbitBuilder是SocialNetCircularOrbitBuilder的一个实例。用来build每一个SocialNetCircularOrbit对象。
操作演示:
菜单
读取文件建立图:可视化:
查询好友位置:
文字输出轨道结构:
增加新的朋友:
增加新的关系并重整结构:
重新查看图:
删除关系并重整结构:
原结构:
删除操作:
删除关系后:
计算信息熵:
计算信息扩散度:
代码:
思路:主要利用队列+BFS算法,对于起始点:
设置map为<起始点,中心点到起始点的权重>,随后对BFS算法找到的每个点,
设置map为<上一个点,上一个点的权*上一个点到这个一个点的关系的权>。
例如图中存在关系a->b->c, a为中心点,则亲密度为:value(a,b)+ value(a,b)* value(b,c)
计算逻辑距离:
以下各小节,只需保留和完成你所选定的应用即可。
由于使用了strategy模式,
只需要新建一个strategy:Relay4Strategy继承自Strategy接口实现一次分配四个人分配就行了
运行展示:
修改AtomStructure的声明
增加addCentralObject方法
修改AtomCircularOrbitBuilder中的bulidPhysicalObjects方法,在构造中心物体时传入一个集合。
在文件读入时也改成读取中心物体,构造集合之后build。
增加Particle类的工厂方法使其能生成质子和中子
修改toString方法,输出原子核的具体情况。
写一个main方法测试下这些新的改动:
输出结果:
这个改动对于我的实现比较简单,因为之前用的就是有向关系,先前读取关系的时候就是相当于add两次相反的有向关系,这里只需要将改为只增加一个有向关系即可。
改为
写一个单元测试来测试:
为了简单,构造一个十分简单的测试文件如下:
读取文件后判断:
x->a, x->b, x->c, a->d都存在关系
d->b,e->x不存在关系如图:
请在完成全部实验要求之后,利用Git log指令或Git图形化客户端或GitHub上项目仓库的Insight页面,给出你的仓库到目前为止的Object Graph,尤其是区分清楚312change分支和master分支所指向的位置。