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
CircularOrbit
每个接口有相应的文档,标注参数和返回值,及其作用,如下:
/**
* get tracks map
* @param null
* @return map of tracks
/
public Map*
* get center L
* @param null
* @return center L
/
public L getCenter();//获取中心
/*
* get center relation List
* @param null
* @return center relation List
/
public Map
/
* get relation map E and E
* @param null
* @return relation map
/
public Map
/
* public getPosition map
* @param null
* @return position map
/
public Map
/
* Create an empty circularOrbit.
*
* @param type of centraOrbit labels in the circularOrbit, must be immutable
* @return a new empty circularOrbit
/
public static
return new ConcreteCircularOrbit
}
/
* 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
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
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
int number;//轨道数差异
List
//每两个轨道为一组比较,构成TrackDifference,组成一个List
public Difference(CircularOrbit
//构造函数,从两个系统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
//一个继承自JPanel的MyPanel类,可添加CircularOrbit属性,并重写其paint()方法,从而画出相应的轨道系统。
CircularOrbit
public MyPanel(CircularOrbit
//构造函数,传入一个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
效果截图:
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分支所指向的位置。