欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

HIT软件构造__Lab3

程序员文章站 2024-02-09 17:48:28
...

1 实验目标概述
本次实验覆盖课程第 3、5、6 章的内容,目标是编写具有可复用性和可维护 性的软件,主要使用以下软件构造技术:
⚫ 子类型、泛型、多态、重写、重载
⚫ 继承、代理、组合 ⚫ 常见的 OO 设计模式
⚫ 语法驱动的编程、正则表达式
⚫ 基于状态的编程
⚫ API 设计、API 复用
本次实验给定了五个具体应用(径赛方案编排、太阳系行星模拟、原子结构 可视化、个人移动 App 生态系统、个人社交系统),学生不是直接针对五个应用 分别编程实现,而是通过 ADT 和泛型等抽象技术,开发一套可复用的 ADT 及其 实现,充分考虑这些应用之间的相似性和差异性,使 ADT 有更大程度的复用(可 复用性)和更容易面向各种变化(可维护性)。
2 实验环境配置
进入URL
https://classroom.github.com/a/aMg3ti15
按照提示绑定classroom,示建立自己的 Lab3 仓库并关联至自己的学号。

进入网站
https://github.com/rainywang/Spring2019_HITCS_SC_Lab3
从中下载示例文件

建立远程仓库链接:

$ git init命令初始化
$ git remote add origin 添加远程仓库
$ git add.添加文件到仓库
$ git commit –m “”提交
$ git push上传到远程仓库
在这里给出你的GitHub Lab3仓库的URL地址(Lab3-学号)。

3 实验过程
请仔细对照实验手册,针对每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
3.1 待开发的三个应用场景
首先请列出你要完成的具体应用场景(至少3个,1和2中选一,3必选,4和5中选一,鼓励完成更多的应用场景)。
 1.TrackGame
 3.AtomStructure
 5.SocialNetworkCircle
相同点:
都有多个轨道,轨道上有一些物体,轨道上的物体属于一个类,要实现增加轨道,删除轨道,在某个轨道上增加物体,在某个轨道上删除物体,从文件中按照一定规则读取信息,并建立轨道系统。
不同点:
1.TrackGame没有中心物体,AtomStructure和SocialNetWorkCircle有中心物体;
2.TrackGame和AtomStructure轨道上的物体间没有关系,而SocialNetWorkCircle轨道上物体之间,中心物体与轨道物体间均有关系;3.TrackGame每个轨道上只能有一个物体,而AtomStructure和SocialNetWorkCircle轨道上物体个数没有限制;
4.AtomStructure轨道上的物体没有差异,而TrackGame和SocialNetWorkCircle轨道上的物体有差异
5.TrackGame中有多组轨道系统,而AtomStructure和SocialNetWorkCircle中只有一个

分析你所选定的多个应用场景的异同,理解需求:它们在哪些方面有共性、哪些方面有差异。
3.2 基于语法的图数据输入
3.2.1 TrackGame
3个正则表达式:

regex1读入的是运动员的信息,<>中包含<姓名,号码,国籍,年龄,本年度最好成绩>,<>中包含大小写字母,数字,逗号“,” ,小数点“.” 。利用regex1匹配到相应的串,再用split函数,用逗号分割,分割后判断是否合法:

如果合法,利用信息创建Player并添加到存放Player的List中,后续利用这个List编排比赛方案,创建轨道系统。

regex2,regex3分别用来读取Game类型和轨道数,类型为100,200,400之一,轨道数在4-10之间的整数。需要判断合法性并给相应的属性赋值。

3.2.2 AtomStructure
3个正则表达式:

regex1匹配原子名字,或为单独的大写字母,或为两个字母,第一个大写,第二个小写
regex2匹配轨道数,是一个正整数
regex3匹配每个轨道上的电子数目,形如1/2;2/6 表示第 1 条轨道 上 2 个电子,第 2 条轨道上6个电子,将regex3匹配到的串的group(2)用分号“;”分开,再用“/”分开,第一个表示轨道半径,第二个表示电子数。
对每个匹配到的信息进行合法检查,若信息合法,则利用信息对相应的属性赋值,并生成电子,创建轨道系统。

3.2.3 SocialNetWorkCircle
3个正则表达式:

regex1匹配中心点用户信息,<>中信息的形式是<姓名,年龄,性别>,姓名是Word,由多个字母构成,有大写,也有小写,年龄是正整数,姓名是字符串,是“M”““”“F”之一。
regex2匹配关系,关系由两个人名及其亲密度组成,SocialTie ::= <用户 1 姓名,用户 2 姓名,社交亲密度>, 亲密度是范围在(0,1]内的小数,最多 3 位小数位,用户 1 和用户 2 可以是 CentralUser 或 Friend,由于文件中存在空格,于是匹配的时候加入“\s”。
regex3匹配朋友信息,朋友信息和中心用户信息形式一致,但<>中具有多余的空格,匹配时先把空格读入,在录入信息时将空格删去,如下:
information=information.replaceAll("[\s]+", “”);
在创建Person前,先检查合法性,若合法,创建Person并添加关系。

3.3 面向复用的设计:CircularOrbit<L,E>
CircularOrbit<L,E>设计为一个接口,将轨道系统中能通用的方法加入到接口中,并在ConcreteCircularOrbit中实现这些方法。
每个接口有相应的文档,标注参数和返回值,及其作用,如下:
/**
* get tracks map
* @param null
* @return map of tracks
/
public Map<Track, List> getTracks();//获取轨道
/
*
* get center L
* @param null
* @return center L
/
public L getCenter();//获取中心
/
*
* get center relation List
* @param null
* @return center relation List
/
public Map<E, Double> getCenterRelation();//获取L与E的关系
/
*
* get relation map E and E
* @param null
* @return relation map
/
public Map<E, Map<E, Double>> getRelation();//获取E与E的关系
/
*
* public getPosition map
* @param null
* @return position map
/
public Map<E, Position> getPositions();//获取每个物体位置
/
*
* Create an empty circularOrbit.
*
* @param type of centraOrbit labels in the circularOrbit, must be immutable
* @return a new empty circularOrbit
/
public static <L,E> CircularOrbit<L,E> empty() {
return new ConcreteCircularOrbit<L, E>();
}
/
*
* add a track
*
* @param Track track
* @return true or false
/
public boolean addTrack(Track track);//添加轨道
/
*
* remove a track
*
* @param Track track
* @return true or false
/
public boolean removeTrack(Track track);//删除轨道
/
*
* add a centraobject
*
* @L center
* @return true or false
*
/
public boolean addCentra(L center);//添加中心点
/
*
* add object to track
* @param E e,Track track
* @return true or false
/
public boolean addObjectToTrack(E e,Track track);//往指定track中加入物体
/
*
* add relation between centra and an object
* @param E e,double d
* @return true or false
/
public boolean addCentraRelation(E e,double d);//添加中心关系
/
*
* add relation between tow object on track
* @param E e1,E e2,double d
* @return true or false
/
public boolean addRelation(E e1,E e2,double d);//添加关系
/
*
* create an Orbit from reading file
* @param null
* @return false or true
/
public boolean Creat(String Fielname);读入文件
/
*
* change the track
* @param E object,Track t
* @return true or false
/
public boolean transit(Track t1,Track t2);//跃迁
/
*
* move an object on a track
* @param E object, double sitha
* @return true or false
/
public boolean move(E object, double sitha);//在轨道上移动
/
*
* next
* @param null
* @return null
/
public void next();//下一组
/
*
* remove a object on track
* @param object , track
* @return true or false
*
*/
public boolean removeObjectOnTrack(E e,Track t);//指定轨道删除指定物体

ConcreteCircularOrbit的实现:
private L center;//中心点物体
private Map<Track,List> tracks=new HashMap<Track,List>();//轨道及对应轨道上物体集合
private Map<E,Double> centerRelation=new HashMap<E, Double>();//L与E的关系网
private Map<E,Map<E,Double>> relation=new HashMap<E, Map<E,Double>>();//E与E的关系网
private Map<E, Position> positions=new HashMap<E, Position>();//每个点对应的位置
public void initPosition()//依据tracks初始化位置
@Override
public boolean addTrack(Track track)//重写添加轨道
@Override
public boolean removeTrack(Track track)//重写删除轨道
@Override
public boolean addCentra(L center)//重写添加中心点
@Override
public boolean addObjectToTrack(E e,Track track)//重写往指定轨道添加物体
@Override
public boolean addCentraRelation(E e,double d)//重写添加L与E关系
@Override
public boolean addRelation(E e1,E e2,double d)//重写添加E与E关系
@Override
public boolean Creat(String filename)//读入文件,空实现
@Override
public boolean move(E object, double sitha)//物体移动,空实现
@Override
public boolean transit(Track t1, Track t2)//跃迁,空实现
public void outPutOnTrack()//输出每个轨道上的物体

JUnit测试:

3.4 面向复用的设计:Track
Track有两个属性:名字name,半径r,半径r>0,必须重写equals函数和hashcode,否则从tracks的Map中寻找某一个轨道时会出错,无法找到想要的轨道,但实际存在。Track是不可变的,所以轨道上的物体不能作为Track的属性。
private String name;//名字
private int r;//半径
public Track(String name,int r)//构造函数
public String getName()//获取名字
public int getR()//获取半径
@Override
public boolean equals(Object obj)//重写equals
@Override
public int hashCode()//重写hashcode,避免使用Map出错
3.5 面向复用的设计:L
centralObject类作为父类,只有名字name一个属性
所有L均为centralObject,3个应用中的中心物体分别是EmptyCentral,Nucleus,Person,均继承自centralObject类,TrackGame中其实没有中心物体,作为泛型的L,为防止空指针错误,设计一个EmptyCentral表示空中心,EmptyCentral的名字一定是empty,可以通过名字判断中心是否为空。
实际上,除了EmptyCentral和Nucleus都只有一个属性name,只有Person有其他属性
包括名字,年龄,性别:

重写中心物体的equals和hashcode函数:

重写tostring函数,在写GUI时绘制轨道上物体的名字会用到

3.6 面向复用的设计:PhysicalObject
PhysicalObject类作为父类,是一个抽象类,只有一个属性,名字name,方法getName。
重写了equals方法和hashcode方法。

三个应用中的E:Player,Electron,Person均继承自PhysicalObject
Player:
private int number;//编号
private String nation;//国籍
private int age;//年龄
private double bestScore;//最好成绩
private final boolean isDifferent=true;//物体间是否有差异
public boolean isDifferent()
public int getNumber()
public String getNation()
public int getAge()
public double getBestScore()各种属性的Getter
public Player(String name,int number, String nation, int age, double bestScore)//构造函数
@Override
public String toString()//重写toString
@Override
public boolean equals(Object obj)//重写equals
@Override
public int hashCode()//重写hashcode

Electron:
private final boolean isDifferent=false;//物体间无差异
public boolean isDifferent()//获取是否有差异
public Electron(String name)//构造函数
@Override
public String toString()//重写toString

Person:
private String name;//姓名
private int age;//年龄
private String sex;//性别
private final boolean isDifferent=true;//是否有差异
public boolean isDifferent()
public Person(String name, int age, String sex)//构造函数
public String getName()
public int getAge()
public String getSex()//属性的Getter
@Override
public String toString()//重写toString
@Override
public boolean equals(Object obj)//重写equals
@Override
public int hashCode()//重写hashcode
3.7 可复用API设计
3.7.1 double getObjectDistributionEntropy(CircularOrbit c)
计算熵值公式:-∑x(i)ln(x(i)),x(i)=number(i)/sum,number(i)第i个轨道上物体的数量,sum是所有轨道上物体的总数。

3.7.2 int getLogicalDistance (CircularOrbit c, E e1, E e2)
计算逻辑距离,利用BFS进行广度优先搜索,维护一个map,每个E对应一个Integer,初始化为0,建立队列,加入e1,每次取出队首元素并将和该节点有关系的元素入队(如果没有访问过),被放入的元素相应的Integer设置为父结点Integer+1,当取出队首元素为e2,查找成功,返回e2对应的Integer,若一直找不到,则返回-1.

3.7.3 double getPhysicalDistance (CircularOrbit c, E e1, E e2)
计算物理距离,CircularOrbit中记录了每个物体的Position,是以极坐标形式存储的,存储了角度bearing和r,设中心点为o,可以得知oe1和oe2的夹角,以及两边之长,利用余弦定理可以求得e1,e2的距离。

3.7.4 Difference getDifference (CircularOrbit c1, CircularOrbit c2)
求取轨道系统之间的差异,先构造两个轨道之间的差异ADT
TrackDifference<L,E>
int id;//轨道id
int number;//轨道中物体数目差异
boolean flag=false;//物体是否有差异
List listA=new ArrayList();
List listB=new ArrayList();//分别为两个轨道的差异物体的集合,可以容纳多个物体
public TrackDifference(List track1,List track2,int id)
//构造函数,id赋值给轨道id,number=track1.size()-track2.size(),对track1,track2作差,前者减后者得到listA,后者减前者得到listB。
@Override
public String toString()//重写toString,方便打印差异信息

Difference<L,E>:
int number;//轨道数差异
List<TrackDifference<L,E>> list=new ArrayList<TrackDifference<L,E>>();
//每两个轨道为一组比较,构成TrackDifference,组成一个List
public Difference(CircularOrbit<L,E> c1,CircularOrbit<L, E> c2)
//构造函数,从两个系统c1,c2中获取轨道tracks,找到半径相同的轨道track1,track2,以及半径r作为参数,利用TrackDifference(List track1,List track2,int id)构造TrackDifference,并加入到list中。
public void output()//打印差异信息

JUnit测试结果

Difference差异测试:

测试用例是实验指导书上的案例,结果一致。

3.8 图的可视化:第三方API的复用
class MyPanel<L,E> extends JPanel:
//一个继承自JPanel的MyPanel类,可添加CircularOrbit属性,并重写其paint()方法,从而画出相应的轨道系统。
CircularOrbit<L, E> c; //要画出的系统
public MyPanel(CircularOrbit<L, E> c)
//构造函数,传入一个CircularOrbit,对c赋值
public void paint(Graphics g)
//根据c画出图形,根据轨道的半径画同心圆,根据轨道中存的position在每个物体相应的位置标注,遍历关系Map,若两个人有关系,获取两个人的位置,在两个位置间连线。

3.9 设计模式应用
策略模式(Strategy)
在编排比赛方案时,要求实现两种方案,第一种是随机分配,第二种要求成绩好的后出场,同组的成绩好的在内侧跑道。于是定义了strategy类,TrackGameStrategy1和TrackGameStrategy2继承自Strategy类,Strategy:
public List strategy=new ArrayList();
//用于存放不同组的轨道系统,初始为空,由TrackGameStrategy1和TrackGameStrategy2进行填充。
public TrackGame trackgame;
//Strategy和TrackGame的交互,初始时,TrackGame,create()读取文件,构造出一个存放运动员的List,TrackGameStrategy1和TrackGameStrategy2利用该List对运动员进行一定处理,并构建TrackGame,加入到strategy,形成不同的方案
public int nowGame=0;//当前组编号,用于GUI显示

TrackGameStrategy1:
public TrackGameStrategy1(TrackGame trackgame)//传入TrackGame,获取存放Player的List
public void init()//每次从List中取出若干个Player,构建TrackGame,加入到strategy中

TrackGameStrategy2:
public TrackGameStrategy2(TrackGame trackGame)//传入TrackGame,获取Player的List
public void sortList()//对Player的List进行排序
public void init()//按成绩从差到好编排,每次取出若干个Player,成绩好的放在内轨道,成绩差的放在外轨道。
随机方案:

按成绩安排:

3.10 应用设计与开发
利用上述设计和实现的ADT,实现手册里要求的各项功能。
以下各小节,只需保留和完成你所选定的应用即可。
3.10.1 TrackGame
GUI界面:

功能1:读取文件并建立轨道系统,如上图
功能2:GUI可视化。如上图
功能3:增加轨道
输入半径,点击按钮
添加成功
功能4:增加物体
输入信息 ,点击按钮
添加成功
功能5,:删除轨道
输入半径,点击按钮
删除成功
功能6:删除物体

删除成功
功能7:计算熵值

计算正确
功能8:切换显示的组

功能9:编排不同比赛方案,方案1,方案2
方案1
方案2
功能10:改变运动员所在组和轨道

改变前
改变后
功能11:检查合法性,检测最后一个组之前,每个轨道必须满员,一个轨道不能有一个以上运动员
点击按钮

控制台提示:

3.10.2 StellarSystem
3.10.3 AtomStructure
GUI界面:

功能1:读入文件,建立轨道系统,如上图
功能2:GUI可视化,如上图
功能3:增加轨道

功能4:增加物体

功能5:删除轨道

功能6:删除物体

功能7:计算熵

功能8:电子跃迁

跃迁前
跃迁后
3.10.4 PersonalAppEcosystem
3.10.5 SocialNetworkCircle
GUI界面:

功能1:读取文件,建立轨道系统,如上图
功能2:GUI可视化,如上图
功能3:增加轨道

功能4:增加物体

功能5:删除轨道

功能6:删除物体

删除前
删除后
功能7:计算熵值

功能8:增加关系

功能9:删除关系

功能10:计算扩散度
a通过b认识c,则ac亲密度为ab亲密度和bc亲密度乘积,当亲密度低于0.1时,两人不能认识,利用BFS寻找通过某人能认识多少人

功能11:计算逻辑距离

功能12:判断合法性
对轨道上的每一个点,计算中心点到该点的逻辑距离,若逻辑距离是3,则应在第3轨道,若是2,则应在第2轨道,以此类推,若是-1,则不应该出现在系统中,控制台提示错误信息,如果正确,则显示“正确”。

若删去第3轨道

控制台提示正确
3.11 应对应用面临的新变化
以下各小节,只需保留和完成你所选定的应用即可。
3.11.1 TrackGame
任务要求:将原来的单人比赛改为4人接力赛。
设计思想:构建一个新的ADT,类Team,继承自PhysicalObject:

将TrackGame的E改为Team,再相应改变增加物体,删除物体,改变方案的change方法,以及编排比赛方案的Strategy类中相应的方案即可。
public class TrackGame2 extends ConcreteCircularOrbit<EmptyCentral, Team>

效果截图:

3.11.2 StellarSystem
3.11.3 AtomStructure
任务要求:将原子核改为多个物体的集合,即可以有多个质子和多个中子。
在中心物体类Nucleus中加入两个List,分别用来存放质子和中子

测试截图

3.11.4 PersonalAppEcosystem
3.11.5 SocialNetworkCircle
任务要求:为社交关系增加方向性,多轨道中只包含从中心点 向外的社交关系。此时输入文件中 SocialTie ::= <用户 1, 用户 2, 社交亲密度>表示从用户 1 指向 用户 2 的社交关系。如果有代表着从轨道物体到中 心点用户的关系,则自动忽略它们;如果有代表着 从外层轨道向内层轨道的关系,也自动忽略它们。
设计思路:改变添加关系的方法,将原来的加双向关系改为只加单向关系,在GUI的实现中用带箭头的直线画线。

GUI截图

3.12 Git仓库结构
请在完成全部实验要求之后,利用Git log指令或Git图形化客户端或GitHub上项目仓库的Insight页面,给出你的仓库到目前为止的Object Graph,尤其是区分清楚312change分支和master分支所指向的位置。