?
2019年春季学期 计算机学院《软件构造》课程
Lab 3实验报告
姓名 | 刘帅 |
---|---|
学号 | 1170500804 |
班号 | 1703008 |
电子邮件 | [email protected] |
手机号码 |
目录
1 实验目标概述··· 1
2 实验环境配置··· 1
3 实验过程··· 1
3.1 待开发的三个应用场景··· 1
3.2 基于语法的图数据输入··· 2
3.3 面向复用的设计:CircularOrbit· 2
3.4 面向复用的设计:Track· 4
3.5 面向复用的设计:L· 4
3.6 面向复用的设计:PhysicalObject· 4
3.7 可复用API设计··· 4
3.8 图的可视化:第三方API的复用··· 5
3.9 设计模式应用··· 5
3.10 应用设计与开发··· 11
3.10.1 TrackGame· 11
3.10.2 StellarSystem·
3.10.3 AtomStructure· 24
3.10.4 PersonalAppEcosystem·
3.10.5 SocialNetworkCircle· 32
3.11 应对应用面临的新变化··· 38
3.11.1 TrackGame· 38
3.11.2 StellarSystem·
3.11.3 AtomStructure· 39
3.11.4 PersonalAppEcosystem·
3.11.5 SocialNetworkCircle· 39
3.12 Git仓库结构··· 39
4 实验进度记录··· 40
5 实验过程中遇到的困难与解决途径··· 40
6 实验过程中收获的经验、教训、感想··· 41
6.1 实验过程中收获的经验和教训··· 41
6.2 针对以下方面的感受··· 41
1 实验目标概述
本次实验覆盖课程第 3、5、6 章的内容,目标是编写具有可复用性和可维护 性的软件,主要使用以下软件构造技术:
? 子类型、泛型、多态、重写、重载
? 继承、代理、组合
? 常见的 OO 设计模式
? 语法驱动的编程、正则表达式
? 基于状态的编程
? API 设计、API 复用
2 实验环境配置
https://github.com/ComputerScienceHIT/Lab3-1170500804.git
3 实验过程
请仔细对照实验手册,针对每一项任务,在下面各节中记录你的实验过程、阐述你的设计思路和问题求解思路,可辅之以示意图或关键源代码加以说明(但千万不要把你的源代码全部粘贴过来!)。
3.1 待开发的三个应用场景
首先请列出你要完成的具体应用场景(至少3个,1和2中选一,3必选,4和5中选一,鼓励完成更多的应用场景)。
l trackgame
l atomstructure
l socialnetworkcircle
分析你所选定的多个应用场景的异同,理解需求:它们在哪些方面有共性、哪些方面有差异。
共性:均需要轨道、中心物体、轨道物体,轨道均为圆形。需要完成的功能也都十分类似,例如增加轨道、删除轨道、在某一轨道上增加物体、在某一轨道上删除物体、获得轨道的熵值、获得逻辑距离、比较两个类型相同的轨道系统的差异、检查一个轨道系统是否符合规范、并且要做到可视化
差异:
TrackGame没有中心物体,在312change之前一个轨道上只有一个物体。同时它需要我去设计算法,来编排比赛方案,而每个比赛方案都是一个完整的系统,所以比起后面两个,TrackGame可能会出现多个系统,这就意味着需要一个新的数据结构去管理这些系统们。
AtomStructure系统则需要实现给定源轨道、目标轨道,模拟电子跃迁。同时还要我去应用设计模式来记录在电子跃迁之前的状态,能够实现回滚功能。
SocialNetworkCircle系统则需要实现从社交关系日志中恢复该结构,判定每个用户是在哪个轨道上,计算某个处于第一条轨道上的某个好友的“信息扩散度”(即通过该好友可以间接认识多少个朋友,需要考虑社交关系的亲密度因素,具体计算方法请自定),增加/删除一条社交关系(然后重新调整该图的结构),计算两个轨道上的用户之间的逻辑关系
3.2 基于语法的图数据输入
先读取文件的每一行,然后把读到的所有行都放到一个String里面,这是为了防止出现完整内容但是分隔在了两行里面。运用正则表达式,进行文字的匹配,然后利用Matcher.group()方法将读取到的信息匹配、分类,最后利用getter方法将读取到的信息返回,用于后续处理
3.3 面向复用的设计:CircularOrbit<L,E>
circularOrbit我设计成了一个接口,里面包含了只有两个最基础的方法:
+ addObject
+ addTrack
主要起到作用的是concreteCircularOrbit,这个我运设计成了一个具体的类,之后三个应用:trackGame, atomStructure, socialNetworkCircle都是继承自这个类
这个类是可变的,所有的getter都使用了defensive copy所以安全性有保障
3.3.1域
protected L centralObject | /central Object/ |
---|---|
protected List physicalObjects | /a list that saves all the physical objects in the system/ |
protected List tracks; | /a list that saves all the tracks in the system/ |
protected Map<Track, List> trackObjectRelation | a map that saves all the relation between objects and tracks |
protected SortStrategy sst | SortStrategy是iterator模式的实现接口 |
3.3.2 方法:
getter方法都是通过返回defensive copies来防止 rep exposure
public Map<Track, List> getTrackObjectRelation() | TrackObjectRelation的getter |
---|---|
public List getPhysicalObjects() | PhysicalObjects的getter |
public List getTrack() | tracks的getter |
public void setCentralObject(L co) | CentralObject的setter |
public void setPhysicalObject(List po) | PhysicalObject的setter |
public void setTracks(List tr) | tracks的setter |
public void setTrackObjectRelation(Map<Track, List> map) | TrackObjectRelation的setter |
public void SetSortStrategy(SortStrategy sst) | 将Iterator模式delegate到circularOrbit的类的setter |
public boolean addObject(E o) | 将一个物体加入到这个系统中 |
public void addTrack() | 将一个轨道加入到这个系统中 |
public boolean removeTrack(int seq) | 将一个特定的轨道(参数中给出)从系统中移除,并且移除这个轨道上的所有物体 |
public boolean addObject2Track(int seq,E PO) | 将一个特定物体添加到一个特定的轨道上(都在系数中给出) |
public boolean removeObjectFromTrack(int seq, E PO) | 从一个特定轨道上将一个特定物体移除 |
3.4 面向复用的设计:Track
这是一个immutable的类,一旦创建之后就不会改变,没有setter方法
3.4.1 域:
double r | /the radius of the track/ |
---|---|
int seq | /the sequence num of the track/ |
3.4.2 方法
public Track(int seq, double r) | 构造方法 |
---|---|
public int getSeq() | 是这个轨道的sequence number的getter |
public double getR() | 是这个轨道的radius的getter |
public boolean equals(Track t) | 判断两个轨道是否相同,判断的依据是sequence number是否相同 |
public int hashCode() | 重写,返回的是sequence number因为它一定是独一无二的 |
3.5 面向复用的设计:L
可以是任何表征中心点物体的类
3.6 面向复用的设计:PhysicalObject
这个类不可变,因此没有setter方法
我觉得physical object没有什么共性的方法,无非是一些getter,而getter都是根据具体的域来决定的,所以我并没有设置任何方法
3.7 可复用API设计
3.7.1 double getObjectDistributionEntropy(CircularOrbit c)
这个方法是计算一个轨道系统的熵值,轨道系统的熵值计算方法是:
具体实现方法就是依次统计第i个轨道中物体的个数,然后除以总的物体个数得到pi, 其中(0<1<tracks.size())
最后带到这个公式里就可以了
3.7.2 public int getLogicalDistance(CircularOrbit c,E e1,E e2)使用BFS算法计算出e1和e2之间的逻辑距离
3.7.3 double getPhysicalDistance (CircularOrbit c, E e1, E e2)
我选的应用里面没有physicalDistance的实现
3.7.4 public Difference getDifference(CircularOrbit c1,CircularOrbit c2)
将两个系统传入Diffrence的一个实例中,将得到两个系统的不同delegate给Diffrence,最后返回这个diffrence的实例就可以了
3.8 图的可视化:第三方API的复用
我用的是java.awt.Graphics, java.javax.swing中的API
每个应用的可视化都由两个类构成:painter和frame
其中frame是画布,用于设置画出来的窗口的位置和窗口中的布局
Painter是绘画track和physical object的类
3.9 设计模式应用
3.9.1 factory method模式应用
构造 Track、PhysicalObject 等对象时,使用 factory method 设计模式。
Track类我建立了一个工厂类,里面只有一个方法,返回一个Track类型的新的变量
并且设置成了static类型,这样我不用创建一个新的实例都可以直接调用这个方法。
其他的每个应用中我都设置了工厂类:
3.9.2 builder模式应用
构造 ConcreteCircularOrbit 对象时,针对不同应用中所需的不同类型的 L 和 E
我使用了builder模式(这个实验1/3时间都在跟这个模式作斗争)
3.9.2.1 builder类
有一个顶层的抽象类concreteCircularObjectBuilder,里面定义了四个方法:
public abstract void buildCentralObject(L central) | 用于构建circularOrbit中的CentralObject,在各个应用中会调用circularOrbit中自身的setter方法 |
---|---|
public abstract void buildPhysicalObject() | 用于构建circularOrbit中的PhysicalObject在各个应用中会调用circularOrbit中自身的setter方法 |
public abstract void buildTracks() | 用于构建circularOrbit中的tracks,在各个应用中会调用circularOrbit中自身的setter方法 |
public abstract void buildTrackObjectRelation() | 用于构建circularOrbit中的TrackObjectRelation,在各个应用中会调用circularOrbit中自身的setter方法 |
在具体的应用类中可能会有自己独特的域,会添加一些新的buildxxx方法,都是调用circularObject自身的setter方法
3.9.2.2 director类
所有的director类都有两个自己的基本的方法:
public concreteCircularOrbit getCO() | 返回一个concreteCircularOrbit类型的引用,即返回在director中构建的circularOrbit |
---|---|
public void constructCircularOrbit() | 调用了所有的builder里面的方法创建一个新的circularOrbit,因此这个方法应该在上一个方法之前用 |
因此当需要构建一个concreteCircularOrbit的时候,客户端调用的是director中的constructCircularOrbit(),之后通过getCO()得到一个concreteCircularOrbit
3.9.3 facade模式应用
在设计可复用 API 时,遵循 fa?ade 设计模式,将所有 API 放置在 helper 类 CircularOrbitAPIs
我将helper和CircularOrbitAPIs合并了
也就是说我的CircularOrbit中不仅有3.7中所述的四个方法,还有所有功能的包装类
比如atomStructureAPIs里面就有Transition、RollBack等功能的包装类,而我的UI中只是简单地调用了atomStructureAPIs的方法,这样就能完全将客户端和我内部的设计了隔离,保证安全性。
3.9.4 iterator模式应用
请使用 Iterator 设计模式,设计迭代器
我有设计出这个接口,但是后面应用中没有可以用到的地方,所以我只在concreteCircularOrbit中有实现
我设计了一个Iterator类型的类MyIterator
这个类中我用了一个全局变量num来记录我所遍历过的对象的个数,
其中有两个重写的方法:
public boolean hasNext() | 返回num是否小于TrackObjectRelation.size(),如果小于,说明还有对象没有被遍历到,否则就是都遍历到了。 |
---|---|
public E next() | 返回TrackObjectRelation的第num+1个对象 |
而concreteCircularOrbit还实现了iterable接口,重写的方法返回的是MyIterator类型的对象,这样二者就可以建立起联系了。
我还在MyIterator中Delegate了一个SortStrategy类的变量,这个类的功能是将TrackObjectRelation中的track按照sequence number的大小进行排序,因为实验指导书上有说从内到外遍历排序,本身是想做一个strategy模式的,因为有的circularOrbit轨道内部是有排序的,有的是没有的,而这正好与strategy模式相符。但是后面一方面因为来不及了另一方面也是因为这对于实现应用并没有什么实质性的帮助,因此就没再继续实现下去了。
3.9.5 strategy模式应用
在 TrackGame 应用中,请使用 strategy 设计模式实现多种不同的比赛方案编 排策略,并可容易的切换不同的算法
3.9.5.1 顶层接口
顶层接口:GameStrategy,里面只有一个方法:
public List<List<List>> generateGame(List PhysicalObjects, List tracks);
这是重构过的方法,因此返回类型是List<List<List>>
最内层的List存放单个轨道上所有的Athlete,即这个List代表一个轨道
次内层的List存放一个系统中的所有轨道,即这个List代表一个系统
最外层的List存放所有的通过读单个文件生成的系统
3.9.5.2 实现类
RandomGameStrategy即随机产生一个游戏策略,主要使用的是随机数生成,详见3.10
RankBasedGameStrategy,根据运动员的最好成绩进行排序,约快的人的跑道被安排在越中间,详见3.10
3.9.6 memento模式应用
在 AtomStructure 应用中,使用 state 和 memento 设计模式管理电子跃迁 的状态,并可进行状态的恢复
3.9.6.1 State类
List num | 记录一共有多少个电子 |
---|---|
Map<Track, List> | 记录当前的电子在当前轨道的分布情况 |
3.9.6.2 memento类
将State delegate进Memento,有一个方法:
public State getState() | 这是delegate进Memento的State的getter,用于在恢复的时候返回已经记录的状态 |
---|---|
3.9.6.3 originator类
同样,Originator类也用了delegation,将State类delegate进来,有三个方法:
public void setState(State state) | 一个delegate到Originator类中的State的一个setter |
---|---|
public Memento save() | 将state包装成一个memento类型的变量然后传给CareTaker用来记录CareTaker里面存在有一个专门的数据结构用来存储memento类型的变量,用于恢复 |
public void restore(Memento m) | 将从CareTaker中返回的Memento类型输出,表示已经做过数据恢复这件事情 |
3.9.6.4 careTaker类
里面只有一个数据结构用于存放已经经历过的状态,接收从Originator发来的Memento类型的状态然后存储在一个List中,恢复时就返回最后一个元素,相当于一个栈
3.9.7 template模式应用
在使用diffrence的时候我使用了Template模式
Diffrence类
最高层的抽象类是Diffrence类,后面三个应用针对各自的特征进行相应的的复用,里面定义了四个抽象方法和一个final的方法:
public abstract void getTrackDiffrence() | 计算Track的个数的不同 |
---|---|
public abstract void getPhysicalObjectDiffrences() | 计算每个track上各个PhysicalObject的不同 |
public abstract void getTrackObjNumDiffrences() | 计算每个track上PhysicalObject的个数的不同 |
public abstract void outputDif() | 将前三个方法修改的数据结构进行整合,以字符串的形式进行输出 |
public final void getDiffrence() | 规定了前四个方法的执行顺序,相当于一个template |
3.10 应用设计与开发
3.10.1 TrackGame
3.10.1.1 TrackGame(CircularOrbit)
Override:
public boolean addObject2Track(int seq, Athlete a) | 由于这个实现中一个轨道最多只有一个运动员,所以addObject2Track需要重写,需要加一个判定这个轨道上是否有运动员,如果有运动员那么就不能添加。 |
---|---|
Publicboolean removeObjectFromTrack(int seq) | 与第1个相同,一个轨道只有一个元素,所以一旦移除就直接设置为null |
public boolean removeTrack(int seq) | 这个类中我存储PhysicalObject是按照这个运动员所处轨道的顺序由小到大存储的,即在第一条轨道上的运动员处在list的第一个元素的位置,在第二条轨道上的运动员处在list的第二个元素的位置,所以removeTrack中将physicalObject从轨道中移除需要按照Track中的顺序来移除,因此也需要重写。 |
3.10.1.2 Athlete(PhysicalObject)
Added Fields
int number | /the number of the athlete/ |
---|---|
String nationality | /the nationality of the athelte/ |
int age | /the age of the athlete/ |
double bestScore | /the year‘s best score of the athlete/ |
Override
public boolean equals(Athlete a) | 判断两个athlete相同的依据:两个人所有的域都相同 |
---|---|
public int hashCode() | 返回的是这个athlete的编号,因为这一定是独一无二的 |
public String toString() | 将所有的变量直接合并,因为都是基本数据类型,然后返回 |
3.10.1.3 Reader
Fields
简单来说就是文件中不同类型的信息(比如姓名、国籍等等)分别建立一个数据结构存储,最后再设置getter,这样所有的信息都可以通过这个类得到
private List athleteNames | /A list that save the name of athletes/ |
---|---|
private List nationalities | /A list that save the number of athletes/ |
private List ids | /A list that save the id of athletes/ |
private List bestScores | /A list that save the bestscore of the year of athletes/ |
private List gameTypes | /A list that save the gametype of the game/ |
private List trackNum | /A list that save the tracknumbers of athletes/ |
private int size = 0 | /A list that save the tracknumbers of athletes/ |
Override
public void readFile(File in)
这个方法利用正则表达式读取文件,然后将读取到的信息放入上述所列出的所有fields
Methods Added
上述所有fields的getter
3.10.1.4 Builder Pattern
3.10.1.4.1 trackGameBuilder
Added Fields
private TrackGame CO | 即将被build的一个circularOrbit |
---|---|
private List tracks = new ArrayList<>() | 即将作为参数传给buildTrack方法的List |
Override
除了buildTrackObjectRelation 方法之外其他所有的buiild方法就是简单的调用trackGame中的setter
public void buildTrackObjectRelation()
这个方法是在调用过buildPhysicalObject() 和 buildTracks()之后再调用的
因为之前有说过physicalObject中我是按照这个运动员在轨道中出现的顺序进行排列的,所以直接将轨道和physicalObject一一对应在trackObjectRelation中进行映射就可以了
3.10.1.4.2 trackGameDirector
Fields
private atomStructureReader reader | Reader delegation |
---|---|
private atomStructureBuilder builder | Builder delegation |
Methods
没有新的重写的方法,然后constructCircularOrbit就是直接调用所有的builder类中的build方法,注意一下调用的顺序就行
3.10.1.4.3 TrackGameHolder
因为这个应用可能会有多个不同的系统,所以我就设置了一个trackGameHolder在里面设置一个专门的数据结构来管理所有的系统,并且调用director和reader建立concreteCircularOrbit
Fields
private TrackGameReader reader | /reader delegated in the class/ |
---|---|
private String input | /the file to be read by the reader/ |
private GameStrategy<Game, Athlete> gs | /the game strategy: randomized/rankbased/ |
private TrackGameDirector director | /director delegated in the class/ |
private List trackGames | /a list that saves all the trackGames/ |
private TrackGameFrame tfg | /a drawer that draws pics/ |
private TrackGameBuilder builder | /a builder that builds concreteCircularOrbits/ |
Methods
public void generateGames() | generate games, invoking methods in director and builder |
---|---|
public List getTrackGames() | getter of trackGames |
public void Draw() | a wrapper that draws pic |
public void exchange() | exchange the tracks of two athletes, if wrong input, report an error |
private List getAthletes() | convert the information in String from reader to Athlete instances and then add them to a list |
3.10.1.5 API Reuse
3.10.1.5.1 AthletePainter
Fields
private int px,py | the x-coord and y-coord of the central Object to be drawn |
---|---|
private int x,y | the x,y-coord of center of the center of the track |
private int radius | the raidius of the track |
private int radiusDelta | 下一个track的半径等于上一个的半径+RadiusDelta |
private String name | the name of the central object |
private List> group | the description of the system, the every member in the list denotes an athlete in a track and it is permuted according to its appearance in the tracks |
Methods
public void drawAthlete(Graphics g,List Namecont, List scoreCont) | 画出代表athletes的点 |
---|---|
public void paintTrack(Graphics g) | 画出轨道 |
public void paint(Graphics g) | 调用上面两个方法,画出整张图 |
3.10.1.5.2 AthleteFrame
这是一个画布,只有一个方法:
public void drawTrackGame(List groups, int num)
功能就是使用AthletePainter,通过将描述这个系统的数据结构传进去来画图
画出来的效果是这样的:
3.10.1.6 TrackGameDiffrence
Fields
private int trackNumDif; | /the track number diffrence/ |
---|---|
private List trackObjNumDif | /a list that saves the diffrence of object numbers on a single track/ |
private List PhysicalObjectDif | /a list that saves the physical object diffrence on a single track/ |
private concreteCircularOrbit<Game, Athlete> t1,t2 | /two circular objects delegated in the class/ |
Methods
没有添加新的方法,仅仅是重写方法,基本就是做简单的比较,注意一下要区分二者的轨道数量个数,不然可能会有空指针异常,然后如果两个运动员不同的话我仅仅让他输除了这两个运动员的名字的区别。
以下是Diffrence跑出来的结果:
3.10.1.7 Strategy模式应用
3.10.1.7.1 RandomGameStrategy
在安排比赛策略的时候就是简单的从0到athletes.size()-1个数中随机取出一个数,然后找到对应的运动员,将他安排到轨道上
轨道的选择是按照顺序的,即第一个被选出来的运动员到第一个轨道上,第二个被选出来的运动员安排到第二个轨道上,以此类推,直到所有轨道都被分配了运动员,那就安排第二场比赛。
3.10.1.7.2 RankBasedGameStrategy
在安排比赛策略的时候首先要对运动员的最好的成绩进行排序按照成绩从好到坏进行排序,然后选择最中间的轨道,依次向两边扩展地安排运动员比如:
假设有五名运动员和五个轨道,对运动员根据成绩进行排序
首先安排第一个运动员到第三条轨道上,第二个运动员安排到第二条轨道上,第三个运动员安排的第四个轨道上等等,以此类推。
考虑到运动员可能会比轨道多,所以用一个List<List>进行存储,内层list是一个轨道上所有的运动员,外层list是所有的轨道
如图所示。
然后分组的时候将每一个list中位于同一位置的人划分到一个组
而如果运动员总数不是5的倍数,有地方是空着的也是可以接受的。
3.10.1.8 circularOrbitOrbitAPIs
我将所有的功能都在这个API中进行包装,在main中只调用这个API中的方法
Fields
private TrackGameHolder holder | /trackGameHolder delegated in the class/ |
---|---|
TrackGameFrame tfg | /trackGameFrame delegated in the class/ |
private String Input | /the file to be read in/ |
private List trackGames | /the list that saves trackGames/ |
Methods
public void removeTrack() | wrapper of trackGame.rmeoveTrack() |
---|---|
public void removeObjectFromTrack() | wrapper of trackGame.rmeoveTrack() |
public void addObject2Track() | Wrapper of trackGame.addObject2Track() |
public void RandomGame() | wrapper of RandomGameStrategy.generateGame() |
public void RankBasedGame() | wrapper of RankBasedGameStrategy.generateGame() |
public void exchangeAthletes() | wrapper of trackGameHolder.exchange() |
public void CalculateLogicalDistance() | wrapper of this.getLogicalDistance() |
public double getObjectDistributionEntropy(concreteCircularOrbit<Game, Athlete> CO) | Override |
public int getLogicalDistance(concreteCircularOrbit<Game, Athlete> CO, Athlete e1, Athlete e2) | Override |
public Diffrence getDifference(concreteCircularOrbit<Game, Athlete> C1, concreteCircularOrbit<Game, Athlete> C2) | Override |
3.10.2 AtomStructure
3.10.2.1 AtomStructure(CircularOrbit)
Fields
因为这里面每个电子都是相同的,所以physicalObject就没有必要维护了,只需要维护一个List,里面存储的是每个轨道的电子个数就可以了
List phyObjNum | 存储各个轨道电子个数的list |
---|---|
CareTaker careTaker | Momento模式 |
Originator originator | Momento模式 |
int electronNum | 电子个数总数 |
Momento模式已经在上文解释了,这里就不再多赘述了
Methods
public boolean electronTransition(int trackNum1,int trackNum2) | 实现电子跃迁的方法,将trackNum1中的电子减少一个,trackNum2中的电子多一个就行了。在跃迁之前会记录当前状态。 |
---|---|
public void rollback() | 回滚功能 |
private void savesave() | 内部方法,应用momento模式 |
public String distributionToString() | 将电子分布情况以字符串的形式输出,用于绘图 |
public boolean addObject2Track(int seq, int num) | 因为这里每个电子都是同样的物体,所以要重写将电子添加到轨道的方法,只需要修改那个记录每个轨道电子个数的List就可以了 |
public boolean removeObjectFromTrack(int seq, int num) | 同上,需要重写 |
public boolean removeTrack(int seq) | 同上,需要重写 |
public void addTrack() | 同上,需要重写 |
3.10.2.2 Electron(PhysicalObject)
很明显,electron每个实例都是一样的,所以很简单,因为是immutable,所以不能有setter方法
Fields
private String name = "electron" | 电子的名字(其实觉得没什么必要但是还是走个形式吧,强迫症患者实锤) |
---|---|
private int trackNo | 电子的轨道序号(跃迁后直接扔掉这个电子,所以可以在里面设置轨道号并且无需修改) |
方法就是这两个域的getter
3.10.2.3 atomStructureReader(Reader)
与上一个应用的reader雷同,无非就是修改了存储信息的数据结构
Field
private String elementName | 中心原子的名字 |
---|---|
private List electronNum | 每个轨道电子的数量 |
private int trackNum | 轨道的数量 |
private List tracks | 轨道的List,我直接在reader里面读进来的时候转换成实例了,这样后面就能直接用了,不用再进行转化了,不然每个用到它的类都要转换一遍太麻烦了。 |
Methods:
继承自接口的readFile和上述域的getter。
3.10.2.4 BuilderPattern
3.10.2.4.1 atomStructureBuilder
与前一个应用雷同,所有的build方法调用了atomStructure的setter
3.10.2.4.2 atomStructureDirector
与接口中所述的雷同
首先将builder和readerdelegate进来,
然后将从reader中得到的信息进行整合
最后调用builder中的build方法构建atomStructure
3.10.2.5 API Reuse
3.10.2.5.1 atomStructurePainter
Fields
private int px,py | the x-coord and y-coord of the central Object to be drawn |
---|---|
private int x,y | the x,y-coord of center of the center of the track |
private int radius | the raidius of the track |
private int radiusDelta | 下一个track的半径等于上一个的半径+RadiusDelta |
private String name | the name of the central object |
private List> group | the description of the system, the every member in the list denotes an athlete in a track and it is permuted according to its appearance in the tracks |
methods
public void drawAtom(Graphics g) | 绘制原子 |
---|---|
public void paintComponent(Graphics g) | 绘制轨道 |
public void drawElectron(Graphics g, int num,int k) | 绘制电子 |
public void paint(Graphics g) | 调用上述三个方法绘制整个图 |
3.10.2.5.2 atomStructureFrame
跟上面一个应用基本一样,这里不多赘述
3.10.2.6 diffrence
与上一个应用基本一样,这里不多赘述
3.10.2.7 atomStructureAPIs
这个类同样是进行了包装,所有的功能在这里面都有一个方法实现,而main中的所有case都调用这个类中的方法,保证内部实现不暴露给外部。
这个类设计的时候我为了有普适性,自己写了好几个原子的文件
Fields
private atomStructureReader reader | Delegate进来的reader,主要功能是读文件 |
---|---|
private atomStructureDirector director | Delegate进来的director,主要功能是构建atomStructure |
private List atomStructures | 一个数据结构专门用来存储atomStructure,因为可能会构建好多个atomStructure |
private atomStructureFrame asf | Delegate进来的画图用的类 |
private Map<String, String> contents | 用来将我写的原子文件的路径和它的名字映射起来,方便客户端操作 |
private List elements | 是我自己构建的文件中都包含有哪些元素 |
Methods
public void Transition() | atomStructure中实现电子跃迁的方法的wrapper |
---|---|
public void generateAtom() | 生成一个原子结构,从我自己写的文件里面选 |
public void EntropyWrapper() | 计算系统熵值方法的wrapper |
public void addTrack() | Wrapper of atomStructure.addTrack |
public void removeTrack() | Wrapper of atomStructure.removeTrack |
public void removeObjectFromTrack() | Wrapper of atomStructure.removeObjectfromTrack |
public void addObject2Track() | Wrapper of atomStructure.addObject2Track |
private int getSize(Map<Track,List> map,List tracks) | 辅助方法,计算熵值时需要知道一共有多少个电子,这个方法就是统计电子个数的方法 |
public void DiffrenceWrapper() | getDiffrence的wrapper |
以及四个override的方法
3.10.3 SocialNetworkCircle
3.10.3.1 socialNetworkCircle(circularOrbit)
fields:
private List degrees | 记录各个好友的亲密度 |
---|---|
private List<List> relations | 用来描述好友之间的关系 |
private Map<Track, List> r | 用于记录每个轨道上的好友 |
private FriendshipGraph graph | 用了lab2的graph,得到每个好友到用户的最短距离用来构建circularOrbit |
Methods:
上面所列出的域的getter和setter,没有其他的方法。
3.10.3.2 Friend(PhysicalObject)
Field
String name | 好友的名字 |
---|---|
String gender | 好友的性别 |
int age | 好友的年龄 |
Methods
上面的域的getter,因为不可变,所以没有setter
3.10.3.3 CentralUser(PhysicalObject)
内部构造与Friend类似,可以说只是换了个名字罢了。
3.10.3.4 socialnetworkCircleReader(reader)
与之前的应用的reader类似,换了数据结构来存储信息,换汤不换药。
Field:
private List friendsName | 记录每个好友的名字的List |
---|---|
private List friendsAge | 记录每个好友的年龄的List |
private List friendsGender | 记录每个好友的性别的List |
private List<List> socialTies | 记录好友之间关系的List,内层的list代表的是特定的两个人之间的关系,外层list记录了所有的关系 |
private List socialTieDegree | 记录关系的亲密度的List |
private String centralUserName | 中心用户的名字 |
private int centralUserAge | 中心用户的年龄 |
private String centralUserGender | 中心用户的性别 |
Methods
上述各个域的getter和重写的readfile,readfile读取文件之后更新这些数据结构。friendsAge,friendsName和friendsGender是一一对应的,相同位置上记录的是同一个人的信息
socialTies和socialTieDegree是一一对应的,相同位置上记录的是同一段关系的信息
3.10.3.5 BuilderPattern
3.10.3.5.1 socialNetworkCircleBuilder
与之前的builder思想一致,就是fields不同,导致方法中的getter不同
这里就不多赘述了,具体域的功能跟reader中返回的大同小异。
3.10.3.5.2 socialNetworkCircleDirector
这个类可以说是我这个系统中最重要的一个类
Fields
private String input | 需要读取的文件 |
---|---|
private SocialNetworkCircleReader reader | Delegate进来的reader,主要功能是读文件 |
private socialNetworkCircleBuilder builder | Delegate进来的builder,主要功能是建立socialNetworkCircle |
private List friends | 存储所有的好友,会作为参数传到builder的方法里 |
private FriendshipGraph graph | 辅助类,用来构建circularOrbit |
private List degrees | 各个关系的亲密度 |
private List<List> relationship | 好友之间的关系 |
private List tracks | 所有的轨道的List |
private Map<Track, List> trackObjectRelation | 好友在轨道上的分布情况 |
Methods
public socialNetworkCircleDirector(SocialNetworkCircleReader reader,String input) | 调用各个内部方法来初始化,将所有的信息通过lab2的辅助类和reader得到的信息进行整合和处理,建立各个数据结构作为参数传入builder |
---|---|
private void init() | 内部方法,将关系和好友的两个数据结构进行比较,首先在好友的数据结构中删去无法到达中心点的数据结构,调用的是辅助类中的getDistance方法,然后在关系中删除和已经经过筛选的好友相关的关系,最后整理两个数据结构。 |
public socialNetworkCircle addRelation | 基本就是重写一遍init,只是读文件进来的那个存有关系的string做了修改,加了个关系 |
private void generateTrackObjRelationship() | 内部方法,将字符串形式的信息读进来转换成类,并放入一个Map中 |
private void generateTracks(int max) | 根据距离中心点最远的好友的距离生成轨道 |
3.10.3.6 API Reuse
与前面几个应用雷同
3.10.3.7 Diffrence
与上面几个应用雷同
3.10.3.8 socialnetworkAPIs
与上面几个应用雷同,都是包装函数,就不展示了。
然后需要提一下的是信息扩散度,我就拿所有跟中心点相连的边的亲密度相加然后计算了一下平均值
3.11 应对应用面临的新变化
3.11.1 TrackGame
主要就是将GameStrategy的返回类型修改为List<List<List>>, 内层List是一个轨道上的运动员,中层list是一个系统的运动员,外层轨道是所有系统的运动员
运行出来的效果图
3.11.2 AtomStructure
将文件中添加质子和中子的信息,修改文件,修改reader中的数据结构,最后修改绘图API中绘制中心点的方法,添加一个drawString即可
3.11.3 SocialNetworkCircle
利用BFS算法搜索中心点可以到达的结点,用一个数据结构标记,然后在写一个filter()方法筛选关系即可
效果图,跟原先的就不一样了
3.12 Git仓库结构
请在完成全部实验要求之后,利用Git log指令或Git图形化客户端或GitHub上项目仓库的Insight页面,给出你的仓库到目前为止的Object Graph,尤其是区分清楚312change分支和master分支所指向的位置。
4 实验进度记录
请使用表格方式记录你的进度情况,以超过半小时的连续编程时间为一行。
每次结束编程时,请向该表格中增加一行。不要事后胡乱填写。
不要嫌烦,该表格可帮助你汇总你在每个任务上付出的时间和精力,发现自己不擅长的任务,后续有意识的弥补。
日期 | 时间段 | 计划任务 | 实际完成情况 |
---|---|---|---|
4.22 | 实验课 | 了解要求 | 按时完成 |
4.23 | 15:00-17:15 | 设计顶层类 | 没设计完 |
4.24 | 18:00-23:00 | 设计顶层类 | 设计完了 |
4.29 | 18:00-22:00 | 写trackGame,改顶层类 | 改了好多 |
4.30 | 17:00-20:00 | 尝试使用builder模式 | 失败了 一头雾水 |
5.1 | 9:00-23:00 | trackGame+builder模式 | Builder模式能用出来了 |
5.2 | 10:00-22:30 | trackGame+优化builder | 优化了builder |
5.3 | 8:00-23:00 | trackGame | 写完了trackGame |
5.4 | 8:00-00:00 | atomStructure | 写完了 |
5.5 | 0:00-14:00 | 通宵写socialnetwork | 没写完 |
5.5 | 18:00-0:00 | 写socialnetwork,发现之前算法有问题,崩溃 | 修改了之前的算法 |
5.6 | 0:00-2:00 | 写socialnetwork | 写的差不多了,还有test,好困 |
5.6 | 10:00-15:45 | 写socialnetwork | 写完了!!! |
5.7 | 不上课的所有时间 | 写报告 | 写完了 我快死了我觉得 |
5 实验过程中遇到的困难与解决途径
遇到的难点 | 解决途径 |
---|---|
在电子跃迁的时候一直没法正确跃迁 | Debug了很久之后发现由于map和list可变,因此必须备份的时候就备份一个defensive copy 不然修改的内容会修改到备份的地方上去 |
不知道如何构建socialnetwork的图 | 想到了lab2 用了lab2的方法 |
6 实验过程中收获的经验、教训、感想
6.1 实验过程中收获的经验和教训
一定要早点开始写实验,这次熬夜写实验写的我哭了
6.2 针对以下方面的感受
(1) 重新思考Lab2中的问题:面向ADT的编程和直接面向应用场景编程,你体会到二者有何差异?本实验设计的ADT在五个不同的应用场景下使用,你是否体会到复用的好处?
复用的确很方便
(2) 重新思考Lab2中的问题:为ADT撰写复杂的specification, invariants, RI, AF,时刻注意ADT是否有rep exposure,这些工作的意义是什么?你是否愿意在以后的编程中坚持这么做?
愿意,电子跃迁的时候吃过苦头了
(3) 之前你将别人提供的API用于自己的程序开发中,本次实验你尝试着开发给别人使用的API,是否能够体会到其中的难处和乐趣?
难处有很多,没有乐趣
(4) 在编程中使用设计模式,增加了很多类,但在复用和可维护性方面带来了收益。你如何看待设计模式?
设计模式的确非常的有意义
(5) 你之前在使用其他软件时,应该体会过输入各种命令向系统发出指令。本次实验你开发了一个解析器,使用语法和正则表达式去解析输入文件并据此构造对象。你对语法驱动编程有何感受?
好慢啊,读大文件肉眼可见
(6) Lab1和Lab2的大部分工作都不是从0开始,而是基于他人给出的设计方案和初始代码。本次实验是你完全从0开始进行ADT的设计并用OOP实现,经过三周之后,你感觉“设计ADT”的难度主要体现在哪些地方?你是如何克服的?
就是设计的时候构建类和类之间的关系很困难,需要将所有应用都浏览一遍,然后考虑到方方面面,不然复用的时候真的很难
(7) 你在完成本实验时,是否有参考Lab4和Lab5的实验手册?若有,你如何在本次实验中同时去考虑后续两个实验的要求的?
没有……根本没有余力
(8) 关于本实验的工作量、难度、deadline。
这也太多了,太难了吧!!!我五一没怎么吃没怎么睡,五天睡了23个小时还通了个宵用了两个latedays压着点写完报告提交的,我写的真的很奔溃,我觉得是lab2那个量的2倍,我写了5000多行代码,这辈子没写过这么多555。
(9) 到目前为止你对《软件构造》课程的评价。
好难【暴风哭泣】
原文地址:https://www.cnblogs.com/SebastianLiu/p/10853291.html