3.1 待开发的三个应用场景
首先请列出你要完成的具体应用场景(至少3个,1和2中选一,3必选,4和5中选一,鼓励完成更多的应用场景)。
StellarSystem
AtomStructure
SocialNetworkCircle
相同:均包含一个中心物体, 和一系列的轨道,每个轨道上有一系列的物体环绕中心物体,其主要的方法均在这些对象上进行。
不同:在AtomStructure上的环绕物体并没有区别,均为电子,且有电子跃迁的情况发生。StellarSystem中的环绕物体是有具体位置的。SocialNetworkCircle上的物体之间有具体的关系,且图的建立是建立在这写关系之上的。
3.2 基于语法的图数据输入
在每一个轨道系统的具体实现类中各自实现一个readFromFile方法。主要方法是通过“::=”符号将每行分开再根据具体情况进行处理。在StellarSystem上,将其分为中心天体和环绕天体。在AtomStructure中获得中心元素的名字和每一个轨道上的电子个数。在SocialNetworkCircle中获得中心人物和朋友信息,之后获得一系列的关系信息。
3.3 面向复用的设计:CircularOrbit<L,E>
-
根据3.1中叙述的三个系统的共性,我们先设计一个接口CircularOrbit<L,E>,并定义如下方法:
EmptyOrbit():获得一个空的系统
addTrack(Track newtrack):添加新的轨道
deleteTrack(Track track):删除一个轨道
addToCentralPoint(L c):添加一个中心物体
getCentralPoint():获得中心物体
getCentralOrbitRelation():获得和中心物体有关联的的物体。
getTrack():获得所有轨道。
getOrbitObject():获得环绕的物体。
getOrbitOrbitRelation():获得环绕物体之间的关系。
addToTrack(E e,String trackRadius):添加一个新物体到轨道上。
deleteFromTrack(E e,Track track):从轨道上删除一个物体。
addRelationCentralTrack(E e):向中心物体和轨道物体之间添加一个新关系。
addRelationTrackTrack(E e1,E e2):向轨道物体和轨道物体之间添加一个新关系。 -
添加一个新的类ConcreteCircularOrbit<L,E>并实现接口CircularOrbit<L,E>。定义如下属性:
private L CentralObject;
private Set tracks;
private Map<Track,Set> orbitObjects;
private Set relationSet;
private Map<E,E> orbitRelationMap;
之后再实现上面列出的方法。
EmptyOrbit():直接创建一个新的对象即可。
addTrack(Track newtrack):直接加到tracks中
deleteTrack(Track track):直接从tracks中删除
addToCentralPoint(L c):加到CentralObject中即可。
getCentralPoint():获得CentralObject
getCentralOrbitRelation():获得relationSet
getTrack():获得tracks即可
getOrbitObject():获得orbitObjects即可。
getOrbitOrbitRelation():直接获得orbitRelationMap
addToTrack(E e,String trackRadius):直接加到orbitRelationMap中trackRadius对应的track的集合上。
deleteFromTrack(E e,Track track):从orbitRelationMap中track对应的集合中删去e
addRelationCentralTrack(E e):将e添加到relationSet中
addRelationTrackTrack(E e1,E e2):将e1和e2添加到orbitRelationMap中即可。 -
添加一个新的类StellarSystem继承ConcreteCircularOrbit< StellarCentralObject , StellarPhysicalObject>。并实现以下方法:
move(StellarPhysicalObject spo, double sitha):此方法用于将spo移动相应sitha角度。
实现只需将spo相应的角度设置为sitha即可。
readFromFiles(String filename):将输入的每一行按::=分开后判断前半部分是Stellar还是planet,并相应创建新的中心和环绕天体,加入系统中。
getPositionAtTime(double time):此函数用于获得所有planet在time时间处的位置。根据每个环绕天体的运行速度和轨道半径获得角速度,并计算time时刻的角度,进而获得位置。
- 添加一个新的类AtomStructure继承ConcreteCircularOrbit <AtomCentralObject , AtomPhysicalObject>类。并实现以下方法:
readFromFiles(String filename):将输入的每一行分开后,分别获得元素名称和轨道数目,最后获得每一个轨道上电子的个数,依次加入轨道上。
transit (AtomPhysicalObject object, Track t):此方法用于进行模拟电子跃迁。将object从原来的轨道中删除,再将其加入新的轨道t即可。
clone():这是重写的方法。用于复制一个AtomStructure,方便以后使用。
- 添加一个新的类SocialNetworkCircle继承ConcreteCircularOrbit <SocialNetworkCentralObject,SocialNetworkPhysicalObject>类。为便系统的建立,再在当前类中加入以下属性:
Map<SocialNetworkPhysicalObject,Double> centralRelationWeight;
Map<SocialNetworkPhysicalObject,Set> orbitRelationSetMap;
Set friendsSet;
centralRelationWeight用于记录每个关系的权重。
orbitRelationSetMap用于记录每一个环绕物体的关系物体。
friendSet用于记录出现的的所有好友。
并使用如下方法:
cloneFriendSet():用于复制一份friendSet并返回。使用的是clone方法。
modifyCentralWeight(Double value,SocialNetworkPhysicalObject s,String action):用于修改和中心天体有关系的物体的权重。Value是新的权值,s是对应的物体,action是控制添加或消除。因此,若action为“add”,则相应增加centralRelationWeight和relationSet。若为“delete”则相应删除即可。
modifyPhysicalWeight():遇上一个方法类似,只是修改的是环绕物体之间的关系。
getCentralRelationWeight():返回centralRelationWeight即可。
getOrbitRelation():返回orbitRelationSetMap即可。
setCentralRelationWeight(SocialNetworkPhysicalObject spo, Double weight):在centralRelationWeight中将spo对应设置为weight
readFromFiles(String filename):通过分裂获得相应的用户信息,并由此创建对象,并将文件中的关系相应添加到orbitRelationSetMap和centralRelationWeight还有relationSet中。
buildGraph(Set frSet):根据上一个方法建立的关系结构,使用从内向外建立系统的方法,分别将与中心用户有直接或间接关系的用户加入对应的轨道。
- 测试
由于其余方法大都在readFromFile中会用到,所以只测试主要的方法。
ConcreteCircularOrbitTest:测试如下几个方法:AddTrack,DeleteTrack,AddToTrack,DeleteFromTrack,AddRelationCentralTrack,AddRelationTrackTrack。
SocialNetworkTest:测试readFromFile方法。使用文件SocialNetworkCircle.txt进行测试并获得通过。
AtomStructureTest:测试readFromFile方法。使用AtomicStructure.txt进行测试,并获得通过。
StellerSystemTest:测试readFromFile方法。使用StellarSystem.txt进行测试,并获得通过。
3.4 面向复用的设计:Track
-
三个系统使用的轨道属性是大致一致的,所以使用一个轨道类就可以。构造Track类:
获得如下属性:
String radius:表示轨道的半径。
并设计以下方法:
getRadius():用于获得半径的值。
3.5 面向复用的设计:CentralObject -
可以发现,centralObejct的共性有其均有一个名字。因此我们设计以下属性:
String name:该object的名字。
并设计以下方法:
getName():用于获得该名字。 -
设计StellarCentralObject类继承CentralObject类。根据星系模型的特点,增加以下属性:
private final String radius:该物体的半径
private final String weight:该物体的重量
并设计以下方法:
getRadius():获得半径
getWeight():获得重量 -
设计AtomCentralObject类继承CentralObject类。为了确保合法性,设计如下方法:
judgeName(String name):用于判断名字的合法性,即由一个或两个字符组成,若由一个字符组成则为大写,若为两个则第一个字母大写,第二个小写。 -
设计SocialNetworkCentralObject类继承CentralObject类。根据社交中心人物的特点,增加如下属性:
private final int age;
private final String sex;
并设计getAge()和getSex()获得这两个属性。此外,在构造方法中检查sex必为M或F。
3.6 面向复用的设计:PhysicalObject -
可以看到PhysicalObject的主要共性是都有名字。据此建立类PhysicalObject。其包括如下属性:
String name
并设计方法:getName()获得名字。此外,重写toString()方法为返回名字。 -
设计类StellarPhysicalObject并继承类PhysicalObject。根据其性质,增加如下属性:
private final String status;
private final String color;
private final String planetRadius;
private final String trackRadius;
private final String OrbitalRevolutionSpeed;
private final String OrbitalRevolutionDirection;
private Double Angle;
并设计如下方法:
getInfo():用于将所有的属性以字符串的形式输出。
setAngle(double newAngle):用于设置其新的角度,还需要检查newAngle是否在0到360之间。
getAngle(),getTrackRadius(),getVelocity(),getOrbitalDirection(),getPlanetRadius():分别用于获得各个属性。 -
设计类AtomPhysicalObject并继承类PhysicalObject。根据其类的特性,增加如下属性:trackRadius用于表示该电子所在轨道半径。并设计如下方法:
getTrackRadius():用于获得该半径值。 -
设计类SocialNetworkPhysicalObject并继承类PhysicalObject。根据该系统的特点,增加如下属性:
private final int age:用户年龄
private final String sex:用户性别
private Map<SocialNetworkPhysicalObject,Double> weightMap:和其有关系的用户和相应的weight。
并设计如下方法:
getAge(),getSex(),getWeightMap()分别用于获得各个属性。
setNewWeight(SocialNetworkPhysicalObject friend,Double weight):用于输入一个新的关系并记录其权值。
clone():重写用于复制一个新的对象。
3.7 可复用API设计
根据实验要求,我们在类CircularOrbitAPIs<L,E>中实现如下几个方法: -
double getObjectDistributionEntropy(ConcreteCircularOrbit<L,E> c):此方法接受一个系统作为输入,并计算其熵值。主要计算方法是利用公式entropy = -Σp*log§,其中p=该轨道上的物体个数/总的物体个数。利用该公式计算并返回即可。
-
int getLogicalDistance (SocialNetworkCircle c, SocialNetworkPhysicalObject e1, SocialNetworkPhysicalObject e2):这个方法用于计算逻辑距离。由于实现的三个系统中只有SocialNetwork拥有逻辑距离,所以输入系统必须为SocialNetworkCircle及相关对象。因此我们也将这个方法设置为静态方法。我们采用广度优先搜索的方法,从e1开始,将其入队之后依次处理队首的元素,在其有关系元素中查找e2并将其入队。并使用变量distance在每一轮处理的过程中记录距离。若没有查找到e2则即将队首元素弹出。直至查找到e2,此时distance即为返回值。
-
Difference<L,E>getDifference(ConcreteCircularOrbit<L,E>c1,ConcreteCircularOrbit<L,E> c2):这个方法用于获得两个系统的区别。首先定义一个Difference类。定义如下属性:
int DValue:两个系统轨道数的差异。
Map<Integer,String> diffMap:从内向外编号依次增大的轨道相比较的不同之处,由String类型记录。
根据判断输入的两个系统是哪种轨道类型,进行不同的判断。若为StellarSystem,则将两者的轨道按半径排序后比较轨道上行星的异同,并记录。若超出了较小轨道个数,则较小轨道对应处写“无”。若为AtomStructure,则只需记录对应轨道的物体数量差异,若该处轨道不存在则数量以0代替。若为SocialNetworkCircle类,则输出对应处1号轨道不存在于2号轨道上的物体和2号轨道不存在于1号轨道上的物体。若该处不存在轨道,输出空集即可。此时Difference类就建立完成了。
之后,在方法中,由于类型输入便控制两个系统必为同一类型,故只需创建一个Difference类并返回即可。 -
double getPhysicalDistanceBetweenPlanets(StellarSystem c, StellarPhysicalObject e1, StellarPhysicalObject e2):考虑到只有星系模型有物理距离的概念,故本方法直接用于StellarSystem。这个方法用于计算两个行星之间的物理距离。使用余弦定理即可计算。其中角度为两个星球角度差值的绝对值,两条边即为两个星球的公转半径。由公式a^2 = b2+c2-2bc*cosθ即可算得。
-
double getPhysicalDistanceBetweenStarPlanet(StellarSystem c, StellarPhysicalObject e):此方法用于获得行星与中心天体之间的距离。直接返回公转半径即可,比较简单。
-
Track getTrackOfItem(ConcreteCircularOrbit<L,E> c,E e):为了方便以后的编程,我们再添加这个方法。此方法用于获得某个物体所属轨道。实现方法就是将便利轨道并查找物体e,找到了e则找到了轨道。
值得提及的是,为了方便以后使用,我们还在APIs包中实现Tools类,其中有两个静态方法:
- BigDecimal processRadius(String rawDis):此方法用于处理文件中给出的具体数值。
可以看到,文件中给出的数字较大时采用科学记数法,所以若输入字串含有“e”那么我们以字母“e”为界分开字串那么前半部分就是0-10之间的数,后半部分为指数,再将其按相应规则相乘即可。若不含“e”则直接转化为数字即可。 - List sortTrack(Set t):此方法用于给轨道按半径排序。主要步骤是先使用processRadius方法将轨道半径得出,之后再将轨道按半径排序之后存入一个list之中,并返回。
3.8 图的可视化:第三方API的复用
我们在APIs包中建立类CircularOrbitHelper用于对三个系统进行可视化。我们使用java.awt.Graphics包进行可视化。先设置中心物体和环绕物体半径:
private final int CENTRAL_RADIUS=10;
private final int PHYSICAL_RADIUS=5;
之后编写函数initialize()并设置窗口大小为(1000,1000),并设置关闭方式为HIDE_ON_CLOSE,并在初始化方法中调用initialize()方法。
此后分别设置三个内部类均继承JPanel类: - StellarSystemDrawPanel:
该类在初始化方法中接受一个星系系统作为输入。之后实现paint方法。先使用fillOval方法以500,500为中心得到实心圆作为中心物体,之后再以40为起始,30为间隔依次画出轨道。每一次画轨道时获得轨道上物体的角度并计算物体所在位置,并在该位置以PHYSICAL_RADIUS为半径画一个实心圆。 - AtomStructureDrawPanel:
该类接受一个AtomStructure作为输入。并实现paint方法如下:先画出中心物体,之后再根据track半径从小到大的顺序,依次获得每个轨道上电子的个数,并用360/电子个数获得每一次改变的角度。再根据每一次递增的角度获得每一个电子的位置,最后在相应位置绘图。 - SocialNetworkDrawPanel
该类接受一个SocialNetworkCircle类作为输入。实现paint方法:先画出中心物体,再画出每个轨道上的物体,其过程与AtomStructure的绘制过程类似,只不过使用一个Map来记录每一个物体对应的位置。最后利用SocialNetworkCircle的关系Map在每一个对应记录坐标之间连线。
最后对每一个实现的内部类,编写一个visualize方法进行调用。
3.9 设计模式应用
请分小节介绍每种设计模式在你的ADT和应用设计中的具体应用。
- 由于所有的系统均使用相同的track,故只需定义一个track类即可,即track只需一个具体产品类。
对于PhysicalObject类,定义父类PhysicalObject,并定义三个子类AtomPhysicalObject,SocialNetworkPhysicalObject和StellarPhysicalObject,其主要构造在之前已经有叙述。此后再定义一个factory父类,他是一个抽象类,包含一个方法PhysicalObject Manufacture(),用于生产某种PhysicalObject。之后再定义三个子类:AtomFactory,SocialFactory和StellarFactory,分别用于身产AtomPhysicalObject,SocialNetworkPhysicalObject和StellarPhysicalObject,其生产函数为Manufacture()。
对于CentralObject类,其基本方式与PhysicalObject完全一样,不再重复。
2. 在ConcreteCircularOrbit<L,E>类中实现一个迭代器。先定义方法iterator()用于返回一个迭代器Itr()。然后定义一个内部类Itr使用
Iterator接口,并重写hasNext和next方法。对于hasNext方法我们遍历排好序的轨道,若没有遍历到最后一个轨道,则返回true,若遍历到最后一个轨道,如果当前遍历的物体个数小于最后一个轨道上物体的个数,则也返回true,若等于,则说明遍历到最后一个了,返回false。而对于next方法,每次获取当前轨道号和物体号对应的那一个物提,并在获取后将物体号加一以便指向下一个物体,若已加到头,则将物体号清零,轨道号加一。
3. 在stellarSystem应用中,通过decorator模式添加卫星。先在cicularOrbit中定义一个抽象类AddSatelliteDecorator继承ConcreteCircularOrbit<StellarCentralObject,StellarPhysicalObject>类,再在其中定义一个SatelliteMap用于存储卫星。并定义一个抽象方法addSatellite用于添加卫星。
之后,再定义一个类SatelliteStellarSystem继承AddSatelliteDecorator类,并在属性中添一个StellarSystem。之后再在其中重写方法addSatellite(StellarPhysicalObject core,Set satellite),在SatelliteMap中添加卫星。
4. 在 AtomStructure 应用中使用state和memento设计模式管理电子跃迁的状态。在circularOrbit中定义一个类AtomTransition,并在其中定义一个内部类Momento,包含如下几个属性:
private int count:用于记录跃迁的次数。
private Map<Integer,AtomStructure> momento:主要备忘录
private Map<Integer,String> log:跃迁日志
并在其中实现对momento的set与get的方法。
此后在AtomTransition中定义如下属性:
private AtomStructure atom:当前的原子模型。
int count:跃迁次数
private Momento m:要管理的备忘录
并在其中定义跃迁函数。其主体部分是来自于AtomStructure中的transit函数,并在每次跃迁之前先使用之前定义过的clone函数复制一份系统并放入备忘录中。此外还定义几个get函数和stateReset(int num)函数,这个函数用于接收要返回的状态编号并将当前的系统重置为当时的系统。
3.10 应用设计与开发
先在application包中的Application类中定义函数printChoiceMenu(),用于打印系统选择的菜单。再定义printBasicChoice()函数用于打印前面五个共有的选择。此后在main函数中调用print函数并获取选择,并根据选择进行下一步的操作。具体操作在各个小节中分解。
3.10.1 StellarSystem
先定义printStellarBoard()函数,用于打印6-9项stellarSystem独有的功能。其跟printBasicChoice()函数一起构成菜单,效果如下:
此后再定义一个doStellarSystem(StellarSystem s,String choice,Scanner in)函数负责具体每项功能的执行。
-
直接调用StellarSystem中的readFromFile函数即可。
-
创建一个CircularOrbitHelper并调用对应的visualize函数,即可获得可视化结果,截图如下:
-
获得轨道半径并寻找该轨道,若该轨道存在则不做任何操作,若不存在则添加该轨道。此后询问是否添加物品,若是则向该轨道添加物品。主要效果如下:
此时再可视化可见内层多了一个星球。
- 用于删除轨道或其上的物品。先选择删除物品还是轨道。若为物品,则通过api中的getTrackOfItem方法获得其轨道,并删除该物品。若删除轨道,则再tracks中删除该轨道,再将轨道和物品的map中该轨道对应的项删除即可。主要效果:
此时再可视化得到如下结果可见此时少了一个星球和一个轨道,即删去的轨道:
-
直接调用api中的getObjectDistributionEntropy函数即可算得。
-
输入时间后,直接调用StellarSystem的getPositionAtTime(time)函数即可得到位置信息。
演示如下,其中括号内是(半径,角度): -
此时输入两个环绕物体,再利用api中实现的getPhysicalDistanceBetweenStarPlanet方法即可获得两个星体之间的物理距离。演示如下:
-
此时模拟星系模型的运动。此时我们使用一个循环来产生动态图像。每一个时间步使用getPositionAtTime函数获得星球当前的位置信息,并在paint函数中转化为位置的改变。每一个时间步使用TimeUnit.SECONDS.sleep(1);使其停顿一秒,再产生下一个时间步的位置。进而绘制下一张图。
-
检查该系统的合法性。我们编写checkLegalityStellarSystem函数实现这一点。主要检查中心点是否有物体和和每条轨道上是否有一个行星。最后对于排好序的轨道,依次计算相邻轨道半径的距离是否大于两个轨道上行星半径之和。所有的物理距离均使用Tools中的函数processRadius函数进行处理。
3.10.2 AtomStructure
先定义printAtomBoard()函数,用于打印第6项AtomStructure独有的功能。其跟printBasicChoice()函数一起构成菜单,效果如下: -
直接调用AtomStructure中的readFromFile函数即可。
-
思路与stellar类似,使用的是AtomStructureDrawPanel内部类。演示如下:
-
实现思路与atellar类似,区别是在加入物体时不需用户输入名字。
-
与stellar类似,区别是在删除物体时不需要输入物体的名字,在该track上随机选取一个物体删除即可。
-
与stellar类似。
-
用户从键盘输入之前的半径和之后的半径,判断合法性之后创建一个atomTransition实例用于维护跃迁的备忘录,并使用其transit函数进行跃迁。演示如下:
之后再进行可视化得到如下结果可见跟之前的图片相比,第三个轨道的电子有一个跃迁到了第二个轨道。:
3.10.3 SocialNetworkCircle
先定义函数printSocialNetwork,用于打印6-10项SocialNetworkCircle独有的功能。其跟printBasicChoice()函数一起构成菜单,效果如下:
-
直接调用SocialNetworkCircle中的readFromFile函数即可。
-
与之前的几个可视化不同的是,这个系统的画板SocialNetworkDrawPanel需要考虑关系。在之前绘制所有环绕物体时,使用一个map记录下他们所在的位置,之后再利用系统中所记录的关联信息,将有关联的两个物体根据存储的位置进行连线。主要效果如下:
-
与之前的实现类似,在加入新用户需要输入多个信息。
-
与之前的实现过程类似。
-
与之前的实现类似。
-
接受一个用户名作为输入,使用iterator找到该用户之后时用api的getTrackOfItem函数获得track即可。演示如下:
-
先接受一个名字作为输入,再判断是否为直接好友之后找到该好友。对于该好友,使用iterator对每一个不是中心人物直接好友的人计算逻辑距离(使用api中的getLogicalDistance函数),再记下其中距离不是无限大的人,即该直接好友可能认识的人,作为该好友的信息扩散度。演示如下:
-
先接受输入:增加还是删除,再输入两个好友的名字。若这个关系存在,则判断是环绕物体之间的关系还是中心物体和环绕物体之间的关系,并依此调用modifyCentralWeight或是modifyPhysicalWeight函数对关系进行修改。此后使用如下函数:
s.getTrack().clear();
s.getOrbitObject().clear();
对之前建造的图进行清空。最后调用buildGraph函数重新格努新的关系建立新的图。演示如下:
此后再进行可视化,可以看到新的关系已经建立了:
-
计算两个用户的逻辑距离。直接调用api中的getLogicalDistance函数即可。演示如下:
-
检查该系统的合法性。我们编写函数checkLegalitySocialNetwork实现这一点。要实现要求里的检查有效性的条件,我们可以看到任意一个好友到第一个轨道上所有人的逻辑距离的最小值应当等于该好友轨道编号减去第一个轨道的编号(即1)。比如说第四个轨道上任意一个人,其与第一个轨道上的所有人求逻辑距离,若该系统合法,则至少应该有一个人和他的逻辑距离等于4-1=3。再对每一个人做这样的验证,即可。
3.11 应对应用面临的新变化
3.11.1 StellarSystem
要实现将轨道改为椭圆,只需在StellarSystem类中添加一个Map<Track,Map<String,Double>>,用于存储某一个Track上的椭圆轨道的长半径x与短半径y值。再提供两个方法setOvalTrackParam和getOvalParam(),分别用于设置每一个轨道的参数和获得每一个轨道的参数。
3.11.2 AtomStructure
要实现中心元素的扩展,我们在AtomStructure中维护一个新的变量Set neutronsProtons。这样一来,之前AtomStructure继承的ConcreteCircularOrbit类中的CentralObject依然存储元素的名字等基本信息。而新添加的neutronsProtons用于存储新的质子和中子等信息。我们将质子和中子也定义为AtomCentralObject类型,其name变量用于存储该对象为“质子”还是“中子”。针对neutronsProtons,在定义两个方法在AtomStructure中:
addNeutronsProtons(AtomCentralObject a):用于添加一个质子或一个中子。
getNeutronsProtons():用于获得质子和中子的集合。
3.11.3 SocialNetworkCircle
-
首先考虑从外部用户指向中心用户的信息,根据之前的设计,在SocialNetworkCircle的readFromFile函数中,第178行处
else if (parseStr[1].equals(getCentralPoint().getName()))
用于处理第二个用户为中心用户的情况。若要忽略该信息,只需将该判断中的操作注释掉就可以,这样第二个用户为中心用户的信息就被忽略掉了。截图如下: -
再考虑第188行开始的处理两个用户均为非中心用户的情况。由于之前为无向图,所以在保存每个用户的关系的时候,orbitRelationSetMap均是双向保存的,即若a与b有关系,则map中a有b且b有a。此时改为单向关系,那么文件中导入的a→b仅会使a的map中包含b。所以需要注释掉b中添加a的部分。主要修改截图如下:
可见201行到214行部分相关代码均被注释了。
3. 最后在可视化关系的时候还要删去,外部指向内部的关系。所以此时修改APIs包中的CircularOrbitHelper类中的SocialNetworkDrawPanel类。此时在画出代表关系的直线时,需判断关系是否为外部指向内部的,即关系a→b中a所在的轨道数是否大于b所在的轨道数。所以修改代码135行至142行,在其中添加判断轨道数的代码。利用CircularOrbitAPIs类中的getTrackOfItem方法获得两者的轨道并获得半径,若符合删除条件则直接使用continue进行下一次判断。代码截图如下: