实验目的:
本次实验覆盖课程第 3、5、6 章的内容,目标是编写具有可复用性和可维护 性的软件,主要使用以下软件构造技术:
⚫ 子类型、泛型、多态、重写、重载
⚫ 继承、代理、组合
⚫ 常见的设计模式
⚫ 语法驱动的编程、正则表达式
⚫ 基于状态的编程
⚫ API 设计、API 复用
本次实验给定了五个具体应用(径赛方案编排、太阳系行星模拟、原子结构 可视化、个人移动 App 生态系统、个人社交系统),学生不是直接针对五个应用 分别编程实现,而是通过 ADT 和泛型等抽象技术,开发一套可复用的 ADT 及其 实现,充分考虑这些应用之间的相似性和差异性,使 ADT 有更大程度的复用(可复用性)和更容易面向各种变化(可维护性)
需要用到的知识: JAVA文件读写、正则表达式、第三方API复用、设计模式应用。
实验任务:
待开发的三个应用场景
TrackGame
AtomStructure
SocialNetworkCircle
基于语法的图数据输入
基于语法的图数据输入,这部分是要使用正则表达式进行文件内容的读取。
三个场景的正则表达式如下:
TrackGame:
AtomStructure:
SocialNetworkCircle:
以SocialNetworkCircle为例:
首先定义正则表达式。
之后按行读取文件内容,分别用以上正则表达式进行匹配。
匹配到相应信息,就创建相应的对象,加入程序的数据集中。
面向复用的设计:CircularOrbit
CircularOrbit使用泛型编程,作为其他子类的接口。
其中定义了如下函数:
/**
* Use: 创建一个空的 CircularOrbit对象.
*
* @param L为中心物体类,E为轨道物体类。
* @return 返回一个空的 CircularOrbit对象。
*/
public static CircularOrbit empty() {
return null;
}
/**
* Use: 添加一条轨道。
*
* @param 输入一条轨道。
* @return 添加是否成功,若已存在就返回失败,否则成功。
*/
public boolean AddTrack(Track track);
/**
* Use: 删除一条轨道。
*
* @param 输入一条轨道。
* @return 删除是否成功。
*/
public boolean DeleteTrack(Track track);
/**
* Use: 添加一个中心物体。
*
* @param 中心物体。
*/
public void AddCentralObject(L centralObject);
/**
* Use: 在特定轨道上增加一个物体。
*
* @param (1)轨道, (2)物体。 要求轨道和物体已经存在。
* @return 添加是否成功。
*/
public boolean AddPhysicalObjectToTrack(Track track, E physicalObject);
/**
* Use: 增加中心点物体和一个轨道物体之间的关系
*
* @param (1)轨道物体。 要求中心点物体和轨道物体已经存在。
* @return 无
*/
public void AddRelation_CentralToPhysical(E physicalObject, float intimacy);
/**
* Use: 增加两个轨道物体之间的关系
*
* @param (1)轨道物体1,(2)轨道物体2。 要求轨道物体已经存在。
* @return 无
*/
public void AddRelation_PhysicalToPhysical(E physicalObject_1, E physicalObject_2, float intimacy);
/**
* Use: 从外部文件读取数据构造轨道系统对象
*
* @param (1)文件地址。
* @return 无。
*/
public void readFile(String pathname);
/**
* Use: 将 object 从当前所在轨道迁移到轨道 t
*
* @param (1)轨道物体 (2)目标轨道。
* @return 无。
*/
public void transit (E object, Track t);
/**
* Use: 将 object 从 当前位置移动到新的 sitha 角度所对应的位置
*
* @param (1)轨道物体 (2)新的 sitha 角度。
* @return 无。
*/
public void move(E object, double sitha);
/**
* Use: 将 object 从系统中移除
*
* @param (1)轨道物体
* @return 无。
*/
public void remove(E object);
函数作用已写在相应注释中。
面向复用的设计:Track
Track,轨道类。
这个类下没有太多属性,因为三个应用中都不存在半径长度的概念,因此在Track中只有轨道编号的属性。为Immutable不可改变类型。
面向复用的设计:L
面向复用设计。L为中心物体类。
中心物体同样也没有太多属性,除了TrackGame外,SocialNetworkCircle和AtomStructure两个应用的中心物体的共同属性只有name一个,因此抽象出来,成为父类。
面向复用的设计:PhysicalObject
面向复用设计。PhysicalObject为轨道物体类。
与中心物体的情况类似,三个应用中,轨道物体分别为:运动员、电子和朋友。
它们的共同属性只有name一个,因此抽象出来,成为父类。
可复用API设计
可复用API写在CircularOrbitAPIs里,有四个功能:
计算熵值、计算逻辑距离、计算物理距离、返回不同。
这里因为三个应用中不存在物理距离,因此直接返回-1;
计算逻辑距离只对SocialNetworkCircle有效,因此只有在是SocialNetworkCircle才被调用。
查询不同时,有一个类Difference:
它内部记录了轨道数的不同、各轨道上物体数的不同和各轨道物体的不同。
图的可视化:第三方API的复用
可视化部分写在CircularOrbitHelper.java文件里。
这部分主要实现了图的可视化。
首先CircularOrbitHelper继承了JFrame类,有常量窗口图的位置。
这个类中最关键的部分是visualize函数。
它负责生成窗口和面板。输入为一个CircularOrbit,根据其类型来生成对应的图。
如下:
之后有一个Scean类,它继承了面板Panel类。负责图的绘制。如下:
首先它获取到图位置信息常量,之后进行绘制。
如下,它判断了输入的图的类型,根据其类型的不同,以不同的方式绘制图。
最后的效果:
设计模式应用
factory method设计模式:
首先,有四个工厂负责各个类型的对象生成。
以PhysicalObject的factory为例:
这里我使用的是static factory的方式,根据输入参数的不同,来对应生成相应的PhysicalObject。
strategy设计模式:
在TrackGame中,使用strategy设计模式,根据输入要求的不同,使用相应的策略进行分组编排(随机/按分数)。
façade 设计模式:
将所有 API 放置在 helper 类 CircularOrbitAPIs 当中。
state 设计模式:
这里我使用一个Map来保存跃迁前的信息。
之后在每次跃迁之前,都对信息进行一次保存更新,这样就能保存下来电子的跃迁前状态,以便于以后的恢复。