2019年春季學期 計算機學院《軟體構造》課程
Lab 3實驗報告
姓名 | 劉帥 |
---|---|
學号 | |
班号 | 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