(防扒小助手)
本人CSDN博客:
https://blog.csdn.net/m0_61753302
本人博客園博客(同步CSDN):
何以牽塵 - 博客園 (cnblogs.com)
https://www.cnblogs.com/kalesky/
如果對你有用的話歡迎點贊關注喲!
???????
目錄
1、實驗目標概述
2、實驗環境配置
2.1 實驗環境
2.2 GitHub Lab3倉庫的URL地址
3、實驗程序
3.1 待開發的三個應用場景
3.1.1 應用場景
3.1.2 共性需求
3.1.3 差異
3.2 ADT識別與設計
3.2.1 任務1:投票型別VoteType
3.2.2 投票項VoteItem
3.2.3 任務3:選票Vote
3.2.4 任務4:投票活動Poll的測驗
3.2.5 任務5:投票活動Poll的實作類GeneralPollImpl
3.2.6 任務6:投票活動Poll的子型別
3.3 ADT行為的設計與實作
3.3.1 任務7:合法性檢查
3.3.2 任務8:采用Strategy設計模式實作靈活的計票規則
3.3.3 任務9:采用Strategy設計模式實作靈活的遴選規則
3.3.4 任務10:處理匿名和實名投票
3.3.5 任務11:采用Visitor設計模式實作功能擴展
3.3.6 任務12:基于語法的資料讀入
3.4 任務13:應用設計與開發
3.4.1 商業表決系統
3.4.2 代表選舉系統
3.4.3 聚餐點菜系統
3.5 任務14:應對面臨的新變化
3.5.1 商業表決應用:可以一次表決多個商業提案
3.5.2 代表選舉應用:遴選規則變化
3.5.3 聚餐點菜應用:取消權重設定、只計算“喜歡”的票數
???????3.6 Git倉庫結構
4、實驗進度記錄
5、實驗程序中遇到的困難與解決途徑
6、實驗程序中識訓的經驗、教訓、感想
6.1 實驗程序中識訓的經驗和教訓(必答)
???????6.2 針對以下方面的感受(必答)
1、實驗目標概述
本次實驗覆寫課程第 2、3 章的內容,目標是撰寫具有可復用性和可維護性
的軟體,主要使用以下軟體構造技術:
- 子型別、泛型、多型、重寫、多載
- 繼承、委派、CRP
- 語法驅動的編程、正則運算式
- 設計模式
本次實驗給定了多個具體應用,學生不是直接針對每個應用分別編程實作,而是通過 ADT 和泛型等抽象技術,開發一套可復用的 ADT 及其實作,充分考慮這些應用之間的相似性和差異性,使 ADT 有更大程度的復用(可復用性)和更容易面向各種變化(可維護性),
2、實驗環境配置
2.1 實驗環境
Intellij IDEA 2022.1(Ultimate Edition)
2.2 GitHub Lab3倉庫的URL地址
略,
3、實驗程序
3.1 待開發的三個應用場景
3.1.1 應用場景
① 商業表決(BusinessVoting)
面向某個商業公司,其內部成員提出某個商業提案(例如“關于投資 xx 專案的提案”),公司董事會的各位董事對其進行實名表決(支持、反對、棄權),各董事在表決中的權重取決于其所持有的公司股票的比例,根據該持股比例對投票結果進行計算,若“支持”票的比例超過2/3,則該提案通過,否則該提案不通過,
② 代表選舉(Election)
針對某次活動(例如哈工大學生代表大會),需要從一群候選人中選出若干人,作為代表參加活動,在該選舉中,提前確定一部分候選人,投票人從已確定的候選人中選取,不可提名新的候選人,計劃選出的代表數量是提前確定的,投票人針對每個候選人匿名選擇“支持、反對、棄權”之一,但選擇“支持”的人數不能高于計劃選出的代表數量,否則為非法票,所有投票人的權重均相等,
③ 聚餐點菜(DinnerOrder)
一群人去餐館就餐,需要從該餐館提供的選單中選擇若干道菜,點菜的數量要大于等于就餐總人數,且小于總人數+5,每個人針對選單上的每一道菜實名表達自己的喜好(喜歡、不喜歡、無所謂),選擇這三個選項的數目無限制,不同人的身份不同,其偏好的影響力會有所不同(例如家庭聚餐時,老人的權重更大、子女的權重更小,見下表黃色部分),所有人表達觀點之后,根據影響力加權計票(喜歡、不喜歡、無所謂分別得分2、0、1),取總得分最高的前k道菜,
3.1.2 共性需求

3.1.3 差異



3.2 ADT識別與設計
3.2.1 任務1:投票型別VoteType
(1)測驗策略
根據VoteType中方法的spec,對checkLegality和getScoreByOption方法進行設計測驗,
checkLegality():
通過對已有的投票型別和未出現的投票型別進行checkLegality的正確性測驗,
getScoreByOption():
給定的投票選項的名稱,通過測驗其getScoreByOption回傳的值是否與給定值相等進行正確性的判斷,
(2)欄位和方法
① spec規約
<1> Rep Invariants
選項應該至少有2個,每個選項的長度不超過5,且不允許出現空格,
<2> Abstract Function
使用options實作的函式對應關系的映射,
<3> Safety from Rep Exposure
必要時使用防御性拷貝防止表示泄露,
② 欄位:
<1> private Map<String, Integer> options = new HashMap<>();
key表示選項名字,value表示該選項所對應的權重,
<2> private String support;
用來記錄哪個選項表示支持,
③ 方法:
<1> private boolean checkRep()
檢查不變性:如果有選項名稱長度超過5或者少于兩個選項,則回傳false,否則回傳true,
<2> public VoteType()
默認使用“支持”(1)|“反對”(-1)|“棄權”(0)這三種投票選項,
<3> public VoteType(Map<String, Integer> origin)
傳入一個給定的Map型別引數,建立一個VoteType型別的物件,
<4> public VoteType(String regex)
根據滿足特定語法規則的字串,創建一個投票型別物件,
<5> public boolean checkLegality(String option)
判斷一個投票選項是否合法(用于Poll中對選票的合法性檢查),
<6> public int getScoreByOption
根據一個投票選項,得到其對應的分數,
<7> public String getSupport()
回傳VoteType中的支持選項的選項名,
(3)測驗結果
測驗結果運行如下:

3.2.2 投票項VoteItem<C>
(1)測驗策略
根據VoteItem中方法的spec,對getCandidate和getVoteValue方法進行設計測驗,
getCandidate():
給定一個投票項,通過觀察呼叫getCandidate()回傳值是否為給定的candidate,
getVoteValue():
給定一個投票項,通過觀察呼叫getVoteValue()回傳值是否為給定的value,
(2)欄位和方法
① spec規約
<1> Rep Invariants
選項名稱不允許出現空格,
<2> Abstract Function
投票選項對候選人型別及String型別的映射
<3> Safety from Rep Exposure
欄位使用private,必要時使用防御性拷貝防止表示泄露,
② 欄位
<1> private C candidate
本投票項所針對的候選物件
<2> private String value;
對候選物件的具體投票選項,例如“支持”、“反對”等
③ 方法
<1> private boolean checkRep()
檢查不變性:如果有選項名稱長度超過5或者出現空格,則回傳false,否則回傳true,
<2> public VoteItem(C candidate, String value)
創建一個投票項物件
<3> public String getVoteValue()
得到該投票選項的具體投票項
<4> public C getCandidate()
得到該投票選項所對應的候選人
(3)測驗結果
測驗結果運行如下:

3.2.3 任務3:選票Vote
(1)測驗策略
根據VoteItem中方法的spec,對getCandidate和getVoteValue方法進行設計測驗,
getCandidate():
給定一個投票項,通過觀察呼叫getCandidate()回傳值是否為給定的candidate,
getVoteValue():
給定一個投票項,通過觀察呼叫getVoteValue()回傳值是否為給定的value,
(2)欄位和方法
① spec規約
<1> Rep Invariants
合法的voteItem
<2> Abstract Function
對應到自己的成員變數
<3> Safety from Rep Exposure
rep均為private,采用防御式拷貝
② 欄位
<1> static private int num
已經產生了多少票,用于為id計數
<2> private int id;
用于標記投票的序號
<3> private Set<VoteItem<C>> voteItems
一個投票人對所有候選物件的投票項集合
<4> private Calendar date
投票時間
③方法
<1>public Vote(Set<VoteItem<C>> voteItems)
創建一個選票物件
<2>public Set<VoteItem<C>> getVoteItems()
查詢該選票中包含的所有投票項
<3>public boolean candidateIncluded(C candidate)
一個特定候選人是否包含本選票中
(3)測驗結果
測驗結果運行如下:

3.2.4 任務4:投票活動Poll<C>的測驗
(1)測驗策略:
由于Poll<C>介面中的方法均為void型別,故采用getter方法對其操作結果進行觀察,比較與預期結果是否相同,通過此策略進行測驗,
(2)測驗方法:
① setInfo()

② addCandidates()

③ addVoters()

④ addVote()

3.2.5 任務5:投票活動Poll<C>的實作類GeneralPollImpl
(1)spec規約
<1> Rep Invariants
quantity為整數
<2> Abstract Function
映射到實際的一次投票活動
<3> Safety from Rep Exposure
用protected防止泄露和必要的防御性拷貝
(2)欄位和方法
① rep
<1> 投票活動的名稱
protected String name;
<2> 投票活動發起的時間
protected Calendar date;
<3> 候選物件集合
protected List<C> candidates;
<4> 投票人集合,key為投票人,value為其在本次投票中所占權重
protected Map<Voter, Double> voters;
<5> 擬選出的候選物件最大數量
protected int quantity = Integer.MAX_VALUE;
<6> 本次投票擬采用的投票型別(合法選項及各自對應的分數)
protected VoteType voteType;
<7> 所有選票集合
protected Set<Vote<C>> votes = new HashSet<>();
<8> 計票結果,key為候選物件,value為其得分
protected Map<C, Double> statistics;
<9> 遴選結果,key為候選物件,value為其排序位次
protected Map<C, Double> results;
<10> 非法選票的集合
protected Set<Vote<C>> illegalVotes = new HashSet<>();
② 方法
<1> private boolean checkRep()
檢查不變數:quantity需要大于0
<2> public void setInfo(String name, Calendar date, VoteType type, int quantity)
設定投票活動的相關資訊
<3> public void addVoters(Map<Voter, Double> voters)
向投票活動中添加voters及其對應的權重
<4> public void addCandidates(List<C> candidates)
向投票活動中添加candidates
<5> protected boolean isLegal(Vote<C> vote)
檢查投票是否合法
<6> public void addVote(Vote<C> vote)
想投票活動中添加選票
<7> protected boolean checkVotes(Set<Vote<C>> votes)
檢查票的合法性
<8> public void statistics(StatisticsStrategy<C> ss)
按規則計票
<9> public void selection(SelectionStrategy<C> ss)
按規則遴選
<10> public String result()
輸出遴選結果
<11> 額外構造的輔助方向(如getter方法)如下
public void accept(Visitor visitor);
public String getName();
public Calendar getDate();
public Set<Vote<C>> getVotes();
public Map<Voter, Double> getVoters();
public Set<Vote<C>> getIllegalVotes();
public List<C> getCandidates();
public int getQuantity();
public VoteType getVoteType();
public String toString();
(3)測驗結果
測驗結果如下:

3.2.6 任務6:投票活動Poll<C>的子型別
(1)BusinessVoting
需要重寫addVote和checkVotes方法
候選物件數量只能為1,必須為實名投票,在計算得票時還需考慮投票人所占權重,
① addVote方法重寫如下:
先檢查vote是否為實名投票,再檢查vote是否合法,若vote不合法或者voters中未包含該vote的投票人,則將該選票列入illegalVotes集合中,

② checkVotes方法重寫如下:
對于votes中的每一個vote,如果vote的voter不包含在該投票活動poll的voters中,則將該選票列入illegalVotes集合中,若在已投票的投票人集合votedVoter中包含了該voter,則將該投票人列入reVoters中,并在之后的計票活動中將重復投票的voter的選票不計入在內,

(2)Election
需要檢查一個投票人投的支持票總數是否小于等于候選物件數量,該投票活動支持匿名投票,所有投票人的權重均相同,
① 增添一個新方法supportCount
用于回傳一個選票內的贊成票數量,

② 重寫addVote方法:
對vote進行判斷,如果vote非法,則將其加入非法選票集合illegalVotes, 如果該投票人投的支持票數量大于候選人數量,也將其加入非法選票集合illegalVotes,

(3)DinnerOrder
需要重寫addVote和checkVotes方法
必須為實名投票,在計算得票時還需考慮投票人所占權重,
① addVote方法重寫如下:
先檢查vote是否為實名投票,再檢查vote是否合法,若vote不合法或者voters中未包含該vote的投票人,則將該選票列入illegalVotes集合中,

② checkVotes方法重寫如下:
對于votes中的每一個vote,如果vote的voter不包含在該投票活動poll的voters中,則將該選票列入illegalVotes集合中,若在已投票的投票人集合votedVoter中包含了該voter,則將該投票人列入reVoters中,并在之后的計票活動中將重復投票的voter的選票不計入在內,

(4)測驗結果
① BusinessVotingTest
Junit測驗結果如下:

② ElectionTest
Junit測驗結果如下:

③ DinnerOrderTest
Junit測驗結果如下:

3.3 ADT行為的設計與實作
3.3.1 任務7:合法性檢查
GeneralPollImpl中將合法性檢查的行為抽取到一個單獨的方法isLegal之中進行合法性檢查:
對一張選票的各投票選項進行考察,若投票選項與候選人數目不相同,則回傳非法值false,
之后對投票選項進行遍歷每一個投票,若所投票的物件不包含在本次投票的候選人之中,或者投票的型別與所投值不相符號,則回傳非法值false,

在addVote方法中對isLegal方法進行呼叫即可:
并將isLegal判斷所對應的非法選票加入到非法選票的集合illegalVotes中,

3.3.2 任務8:采用Strategy設計模式實作靈活的計票規則
(1)BusinessStatistics計票規則
遍歷Vote<Proposal>型別的votes集合中的每一張選票vote,若vote在非法選票集合illegalVotes中,則不計入該選票,
由于BusinessVoting是實名投票,故需要獲取投票人的權重以便進行最終結果的計算,
對每一張選票vote的每一個投票選項VoteItem<Proposal>進行遍歷,在還沒進行遍歷之前先對議案的結果進行初始化,如果投票選項的值為正數,則表示支持,通過加權計入相應proposal的計票結果中,

(2)ElectionStatistics計票規則
遍歷Vote<Person>型別的votes集合中的每一張選票vote,若vote在非法選票集合illegalVotes中,則不計入該選票,
由于Election是匿名投票,所以不需要獲取投票人的權重等資訊,
對每一張選票vote的每一個投票選項VoteItem<Proposal>進行遍歷,在還沒進行遍歷之前先對議案的結果進行初始化,如果投票選項的值為正數,則表示支持,相加計入相應person的計票結果中,

(3)DinnerStatistics計票規則
遍歷Vote<Dish>型別的votes集合中的每一張選票vote,若vote在非法選票集合illegalVotes中,則不計入該選票,
由于DinnerOrder是實名投票,故需要獲取投票人的權重以便進行最終結果的計算,
對每一張選票vote的每一個投票選項VoteItem<Dish>進行遍歷,在還沒進行遍歷之前先對議案的結果進行初始化,對所給的投票值通過加權計入相應dish的計票結果中,

3.3.3 任務9:采用Strategy設計模式實作靈活的遴選規則
(1)BusinessSelection遴選規則
初始化proposal的占比為0.0,本規則對Business進行了擴展,可同時提出多個proposal而不僅限于一個proposal,以適應未來任務的要求,
若對應proposal的計票結果超過了2/3,則將其放入result中傳出給結果,即遴選出最終方案,

(2)ElectionSelection遴選規則
首先將所有候選人放入TreeSet中利用TreeSet進行排序:
Set<Person> set = new TreeSet<>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
if(statistics.get(o1) > statistics.get(o2)) return -1;
else if(statistics.get(o1) < statistics.get(o2)) return 1;
return o1.getName().compareTo(o2.getName());
}
});
set.addAll(statistics.keySet());
Election要求根據支持票數量排序,前k個候選人當選;若有多個候選人的支持票數量相等而無法自然排出前k名,則僅有那些明確可進入前k名的人當選,
因此需要判斷第k名和第k+1名的分數是否相同,若得分不同,則取出這前k名作為最終結果傳遞給result輸出,若得分相同則需要從頭重新遍歷,取出前面與這第k名的分數不同的所有候選人Person,傳遞給result輸出,
double rank = 0.0, score = Integer.MIN_VALUE;
Iterator<Person> iterator = set.iterator();
for(int i = 0; i < Math.min(quantity, statistics.size()); i++) {
score = statistics.get(iterator.next());
}
if(!iterator.hasNext() || statistics.get(iterator.next()) != score) {
iterator = set.iterator();
for(int i = 0; i < Math.min(quantity, statistics.size()); i++) {
results.put(iterator.next(), ++rank);
}
}
else {
iterator = set.iterator();
Person person = iterator.next();
while(statistics.get(person) != score) {
results.put(person, ++rank);
person = iterator.next();
}
}
return results;
(3)DinnerSelection遴選規則
DinnerSelection的遴選規則類似Election的遴選規則,區別在于在其第k個和第k+1個數目相同時,除了取前面與第k個得分不同的候選物件,DinnerSelection還需要在這些得分相同的Dish中選出一部分補齊這k個菜,
因此都在前面使用TreeSet對候選物件的得分結果進行排序,最后DinnerSelection不需要重新對Dish進行遍歷取出,只需要按照順序自然取出這k個菜即可,

3.3.4 任務10:處理匿名和實名投票
應用1提案表決和應用3聚餐點菜是實名投票,而應用2代表選舉是匿名投票,
vote 包中的Vote類預設為“匿名投票”,其rep中沒有出現投票人資訊,為了支持實名投票,在選票 ADT 中額外記錄投票人資訊,構造Vote的子型別 RealNameVote,并使用Decorator設計模式,在Vote基礎上進行擴展,為其系結新的行為,通過委托機制增加到物件上,
構造方法在 Vote 的基礎上,增加 voter 屬性,同時呼叫父類的構造方法,并給予相應的引數串列,

新增 getVoter()方法獲得投票人,getVoteItemsByVoter()通過投票人獲得其對應的選票,并繼承了父類的其他方法,
重寫 equals()方法時,采取判斷資料等價,其中涉及到關于判斷投票選項是否相等時,呼叫父類的equals()方法,依然是參考等價的判斷,
RealNameVote的測驗用例,在 3.2.6 節中的應用1和應用3已經覆寫,這里不做贅述,
3.3.5 任務11:采用Visitor設計模式實作功能擴展
訪問者 Visitor 模式將演算法與其所用的物件隔離開來,為ADT預留一個將來可以作為擴展功能的接入點,外部代碼可以在不改變ADT本身的情況下在需要時通過委托接入ADT,
新增visitor包,并新建介面Visitor及其實作類VotedLegalVisitor,
在實作類VotedLegalVisitor中實作了介面中的兩個方法:
public void visit(GeneralPollImpl<C> poll);
public double getData()
其中visit的作用是直接對投票進行訪問,并進行資訊統計;getData的作用是獲取統計資訊,
在介面Poll中及其實作類GeneralPollImpl中預留了一個accept方法:

使用以下操作呼叫Visitor方法:

對于其測驗用例,在test檔案夾中新建包visitor,新增測驗檔案VisitorTest類,在投票結果下輸出合法選票的比例,
Junit測驗結果如下:

3.3.6 任務12:基于語法的資料讀入
該部分要求用正則運算式接受格式形如{“喜歡”(2)|“不喜歡”(0)|“無所謂”(1)}和{“支持”|“反對”|“棄權”}這兩種字串來創建投票型別VoteType物件,要求每個選項不能出現空白符,且長度≤5,
這里我先用String的split方法先將各個選項分割開:
String[] input = regex.split("\\|");
然后對于每個選項嘗試用正則運算式去匹配,用捕獲組來獲取各個成分,用一個變數state記錄一下這個正則運算式是哪種上面情況(帶數字的/不帶數字,默認權值相等的),state=1為帶數字的,state=2為不帶數字的,
任務要求的兩種正則運算式即為如下兩種:
“\S+”([+-]?\d+)
“\S+”
考慮到java語法,需要使用轉義,因此最終java陳述句中使用如下兩種實作:
Pattern regexNum = Pattern.compile("\\\"(\\S+)\\\"\\(([\\+-]?\\d+)\\)");
Pattern regexNoNum = Pattern.compile("\\\"(\\S+)\\\"");
匹配完成之后使用其內部各成分來創建VoteType的options欄位,
對于其測驗用例,采取正確輸入和錯誤輸入兩種等價類劃分,在正確輸入中,又分別測驗兩種正則運算式,測驗用例在 VoteTypeTest 中新增測驗,
Junit測驗結果如下:

3.4 任務13:應用設計與開發
3.4.1 商業表決系統
首先定義GeneralPollImpl的子類BusinessVoting類,依次設立基本資訊,增加候選提案(只有1個)、投票人、選票,然后開始計算支持率和結果,并呼叫Visitor模式中的合法選票比例方法,最終控制臺輸出結果,若提案沒有通過,則輸出“本次投票未選出合法結果”;否則輸出通過的資訊,

輸出樣例如下:

3.4.2 代表選舉系統
首先定義GeneralPollImpl的子類Election類,依次設立基本資訊,增加候選學生、投票人、選票,然后開始計算支持率和結果,并呼叫Visitor模式中的合法選票比例方法,最終控制臺輸出結果,

輸出樣例如下:

3.4.3 聚餐點菜系統
首先定義GeneralPollImpl的子類DinnerOrder類,依次設立基本資訊,增加候選菜品、投票人、選票,然后開始計算支持率和結果,并呼叫Visitor模式中的合法選票比例方法,最終控制臺輸出結果,

輸出樣例如下:

3.5 任務14:應對面臨的新變化
3.5.1 商業表決應用:可以一次表決多個商業提案
原來并未限定BusinessVoting的rep只能為一個提案,因此可以直接使用原先的BusinessStatistics策略進行統計遴選,下面給出測驗用例:

3.5.2 代表選舉應用:遴選規則變化
該改動要求我們在支持票相同時,比較反對票,反對票少的勝出,問題的關鍵在于,反對票在statistics方法里沒有統計,如果我們想要獲取這個資訊,要么改動介面里的statistics方法和所有Poll子類的statistics方法,要么就得重新統計一遍反對票的資訊,顯然前一種修改的代價太大了,因此這里采用的是重新統計的方法,可以看出這要求遴選策略的直接改動,于是新建一個NewElectionSelection,實作SelectionStrategy,但是它的構造方法需求額外的三個引數,在構造方法里計算一下反對票的資訊:

然后在select方法中重寫一下TreeSet的排序規則:

這里使用兩個變數分別記錄第k個人的贊成票數量和反對票數量,先去用贊成票比較第k+1個人的贊成票數量,如果不等,則按照原來的方法得出結果,如果相等則比較第k+1個人的反對票數量,利用與贊成票相似的規則再次進行判斷:與第k+1個人的反對票數量不等則錄入并輸出結果,反對票數量相等則按順序錄入直到反對票數量與第k個人相等或已經錄入k個人為止,代碼實作如下:
下面給出測驗用例:

原先的排名結果是ABC、GHI,因為測驗給定的條件是:ABC、GHI的支持票數量相等但GHI反對票數量少于ABC,排名結果應為GHI、ABC,因此可知測驗用例的結果與預期相符,
3.5.3 聚餐點菜應用:取消權重設定、只計算“喜歡”的票數
需要創建一個新的計分策略NewDinnerStatistics,把計分部分修改一下之后用的時候拿這個策略去替換原來的DinnerStatistics,
計分部分修改如下:
if(!newDinnerResult.containsKey(dish)) newDinnerResult.put(dish, 0.0);
double value = newDinnerResult.get(dish);
if(voteItem.getVoteValue().equals(voteType.getSupport()))
newDinnerResult.put(dish, value + 1.0);
其余部分不需要變動,測驗結果如下:

可見所有候選物件的權值全變為相同權值了,因此最終結果的排序按字典排序處理,
???????3.6 Git倉庫結構
(1)git log輸出結果

(2)Git GUI中的分支圖

4、實驗進度記錄
|
日期 |
時間段 |
計劃任務 |
實際完成情況 |
|
6.20 |
15:00-18:00 |
完成 VoteType 及其測驗用例 |
按計劃完成 |
|
6.21 |
8:00-11:00 |
完成VoteItem及其測驗用例 |
按計劃完成 |
|
6.22 |
16:00-20:00 |
完成 Vote 及其測驗用例 |
按計劃完成 |
|
6.23 |
19:00-21:00 |
完成Poll介面的具體實作與測驗 |
未完成 |
|
6.24 |
7:00-23:30 |
完成具體計票規則及遴選規則的撰寫 |
按計劃完成 |
|
6.25 |
9:00-次日3:00 |
完成基于語法的資料讀入與app實作 |
未完成 |
|
6.26 |
8:00-21:30 |
完成功能擴展與撰寫報告 |
按計劃完成 |
5、實驗程序中遇到的困難與解決途徑
|
遇到的難點 |
解決途徑 |
|
正則運算式的語法在java陳述句中的具體應用與java相關模塊的具體實作語法了解不夠深入, |
上網查閱相關資料并咨詢同學,并在實作程序中不斷修改與理解, |
|
理解給定的框架, |
在實作 ADT 的途中不斷理解、修改, |
|
對于擴展功能的具體實作策略,如何簡便迅捷地擴展實作感覺頗為棘手, |
自己苦思冥想并咨詢經驗豐富的同學一些相關方法的實作手段, |
|
撰寫相關規約與測驗策略困難,不知道怎么寫 |
自己苦思冥想并在交流群里瀏覽他人心得體會, |
|
對介面與子類的繼承關系不夠熟悉, |
在實作程序中反復嘗試,并在網上查閱相關文獻,加深印象, |
6、實驗程序中識訓的經驗、教訓、感想
6.1 實驗程序中識訓的經驗和教訓(必答)
第一,spec規約對于一個程式員來說十分重要,本次實驗中補全一個ADT的spec的程序十分折磨,因為需要具體分析其各種可能出現的情況并要找出其不變數還要考慮到防止表示泄露的情況,但是當spec補全以后,很多測驗用例的撰寫以及相關呼叫的時候十分方便,因為通過spec我能很快了解到這個ADT的功能是什么,該怎么去呼叫這個spec,
第二,根據已有框架展開撰寫代碼有好有壞,好處在于可以省去構思整體布局的時間,壞處在于如果對于框架理解不到位將產生很復雜且嚴重的連鎖反應,并且少了一次鍛煉且提升自己的機會,并且框架如果設計不夠合理,再去整體改動框架將會十分復雜,甚至所需要的付出的精力不亞于重新實作這個框架,
第三,我本以為本次實驗工程量不會很大,因此前幾天沒有花出足夠的時間去完成實驗,很多作業量都壓到了最后兩天,為了趕ddl而不得不通宵寫實驗,這不僅影響身體健康,也對工程能否按時交付有很大影響,因此我識訓到了凡事都要提早完成的教訓,不能想當然認為什么都可以在ddl之前能夠有足夠的時間去做完,
???????6.2 針對以下方面的感受(必答)
(1)重新思考Lab2中的問題:面向ADT的編程和直接面向應用場景編程,你體會到二者有何差異?本實驗設計的ADT在三個不同的應用場景下使用,你是否體會到復用的好處?
答:面向 ADT 的設計是自底向上,先設計每一部分的操作,封裝后形成 ADT,不受具體資料型別的限制,
面向應用場景的自頂向下,先考慮需求,再考慮用什么方法實作,不同的資料型別會形成不同的代碼,
三個不同場景下的應用,且其中有很多的共性,則要基于共性進行復用,在各自個性的基礎上進行各自的重寫,多次復用比多次重新實作方便簡單很多,也減少了很多無謂的作業量,
(2)重新思考Lab2中的問題:為ADT撰寫復雜的specification,invariants,RI, AF,時刻注意ADT是否有rep exposure,這些作業的意義是什么?你是否愿意在以后的編程中堅持這么做?
答:使撰寫的代碼更加安全和可讀性更強,也便于他人快速理解,愿意這么做,
(3)之前你將別人提供的ADT/API用于自己的程式開發中,本次實驗你嘗試著開發給別人使用的ADT/API,是否能夠體會到其中的難處和樂趣?
答:能夠體會到,難處很高,但也趣味橫生,
(4)你之前在使用其他軟體時,應該體會過輸入各種命令向系統發出指令,本次實驗你開發了一個簡單的決議器,使用語法和正則運算式去決議一個遵循特定規則的字串并據此構造物件,你對語法驅動編程有何感受?
答:基于正則運算式進行決議可以省去大量的通過while和if陳述句進行判斷的操作,方便編程并且提高代碼的美觀性,
(5)Lab1和Lab2的作業都不是從0開始,而是基于他人給出的設計方案和初始代碼,本次實驗中也提供了一部分基礎代碼,假如本實驗要求你完全從0開始進行ADT的設計并用OOP實作,你覺得自己是否能夠完全搞定?你認為“設計ADT”的難度主要體現在哪些地方?
答:能搞定,對各種應用共性和個性的把握,對整體框架結構的設計,對不同應用特性的分析,各種 ADT 中屬性的把握,基于可復用性、正確性、可持續性模式的使用,提高框架的可延展性,
(6)“抽象”是計算機科學的核心概念之一,也是ADT和OOP的精髓所在,本實驗的三個應用既不能完全抽象為同一個ADT,也不是完全個性化,如何利用“介面、抽象類、類”三層體系以及介面的組合、類的繼承、委派、設計模式等技術完成最大程度的抽象和復用,你有什么經驗教訓?
答:將宏觀上共性的操作放在介面中實作,將共性的操作在抽象類中進行宣告,必要的時候可以實作,將各自的個性放在實作類中進行重寫,
繼承和委派都是在現有類的基礎上增加新的屬性、操作的方法,可以提高代碼的復用性,是很好的方法模式,
(7)關于本實驗的作業量、難度、deadline,
答:作業量很大很大,難度較高,deadline時間很緊張,
(8)課程結束了,你對《軟體構造》課程內容和任課教師的評價如何?
答:課程很好,可以說是計算機專業不可缺少的一門課,質量很高,但是學時很少,要求掌握的內容很多,導致很多東西學的不深入不扎實,多快好省反而適得其反,
任課教師還不錯,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/501482.html
標籤:其他
