2021哈工大软件构造Lab3
2021年春季學期
計算學部《軟件構造》課程
Lab 3實驗報告
實驗源碼:https://github.com/1190200817/SC_Lab3
目錄
1 實驗目標概述··· 1
2 實驗環境配置··· 1
3 實驗過程··· 1
3.1 待開發的三個應用場景··· 1
3.2 面向可復用性和可維護性的設計:IntervalSet<L>· 2
3.2.1 IntervalSet<L>的共性操作··· 2
3.2.2 局部共性特征的設計方案··· 3
3.2.3 面向各應用的IntervalSet子類型設計(個性化特征的設計方案)··· 4
3.3 面向可復用性和可維護性的設計:MultiIntervalSet<L>· 7
3.3.1 MultiIntervalSet<L>的共性操作··· 7
3.3.2 局部共性特征的設計方案··· 7
3.3.3 面向各應用的MultiIntervalSet子類型設計(個性化特征的設計方案)··· 9
3.4 面向復用的設計:L· 11
3.5 可復用API設計··· 13
3.5.1 計算相似度··· 13
3.5.2 計算時間沖突比例··· 14
3.5.3 計算空閑時間比例··· 15
3.6 應用設計與開發··· 15
3.6.1 排班管理系統··· 15
3.6.2 操作系統的進程調度管理系統··· 17
3.6.3 課表管理系統··· 18
3.7 基于語法的數據讀入··· 18
3.8 應對面臨的新變化··· 20
3.8.1 變化1· 20
3.8.2 變化2· 21
3.9 Git倉庫結構··· 21
4 實驗進度記錄··· 22
5 實驗過程中遇到的困難與解決途徑··· 23
6 實驗過程中收獲的經驗、教訓、感想··· 24
6.1 實驗過程中收獲的經驗和教訓··· 24
6.2 針對以下方面的感受··· 24
本次實驗覆蓋課程第 2、3 章的內容,目標是編寫具有可復用性和可維護性的軟件,主要使用以下軟件構造技術:
本次實驗給定了三個具體應用(值班表管理、操作系統進程調度管理、大學課表管理),學生不是直接針對每個應用分別編程實現,而是通過 ADT 和泛型等抽象技術,開發一套可復用的 ADT 及其實現,充分考慮這些應用之間的相似性和差異性,使 ADT 有更大程度的復用(可復用性)和更容易面向各種變化(可維護性)。
Eclipse和Git在上一次實驗中已經配置好了。本次實驗無需額外配置環境。
在這里給出你的GitHub Lab3倉庫的URL地址(Lab3-學號)。
https://github.com/ComputerScienceHIT/HIT-Lab3-1190200817
請仔細對照實驗手冊,針對每一項任務,在下面各節中記錄你的實驗過程、闡述你的設計思路和問題求解思路,可輔之以示意圖或關鍵源代碼加以說明(但千萬不要把你的源代碼全部粘貼過來!)。
簡要介紹三個應用:
①值班表管理,一個單位有 n 個員工,在某個時間段內安排值班。每天只能安排一個員工且不能出現無人值班的情況;每個員工需要安排在連續的幾天內。值班表內需要記錄員工的名字、職位、手機號碼,以便于外界聯系值班員。
②操作系統進程調度管理,進程被調度在 CPU 上執行,操作系統決定在各個時段內執行哪個進程。操作系統可掛起某個正在執行的進程,在后續時刻可以恢復執行被掛起的進程。每個時間只能有一個進程在執行,其他進程處于休眠狀態;一個進程的執行被分為多個時間段;在特定時刻,CPU 可以“閑置”;調度無規律,可看作是隨機調度。
③大學課表管理:課程需要特定的教室和特定的教師。假設各周的課表都是完全一樣的,同樣的課程安排將以“周”為單位進行周期性的重復,直到學期結束;一門課程每周可以出現 1 次,也可以安排多次,且由同一位教師承擔并在同樣的教室進行;允許課表中有空白時間段;同一個時間段內可以安排不同的課程;一位教師也可以承擔課表中的多門課程。
相同之處:都包含了具有不同特征的“時間段集合”對象。每個時間段對應一個對象標簽。
不同之處:有三個維度上的差異。①是否允許時間軸上有空白。在應用1中,不允許有空白;在應用2和應用3中,允許有空白。②是否允許不同的 interval 之間有重疊。在應用1和應用2中,不允許有重疊;在應用3中,允許有重疊。③是否包含周期性的時間段。應用 3 中以“一周”為單位重復某個課程,但應用1和應用2中不存在這種情況。
該節是本實驗的核心部分。
這個ADT描述了一組在時間軸上分布的“時間段”,每個時間段附著一個特定的標簽,且標簽不重復。因此共性的方法包括:
由于IntervalSet是一對一的結構,即一個標簽對應一個時間段,因此可以使用Map作為內部的數據結構,定義為:
private final Map<L, time> intervalMap = new HashMap<>();
由于時間段是兩個long型的整數構成的,因此可以定義一個輔助類time表示一個時間段。其中start是時間段的開始,end是時間段的結束,同時要求start一定大于0且start一定比end小。這里time實現了Comparable接口是為了滿足下面MultiIntervalSet中的一些功能。
對于empty()方法,直接返回一個具體實現類。
對于insert()方法,首先判斷非法條件(start<0和start>=end),然后判斷Map中是否包含標簽,若包含該標簽,那只有在重復設置相同時間段時才合法,否則會拋出IntervalConflictException;若不包含該標簽,就直接插入時間段。(注意,這里是允許不同標簽之間存在Overlap的)
對于labels()方法,直接返回Map中所有的鍵構成的集合即可。
對于remove()方法,判斷Map的鍵中是否存在該標簽,若不存在,直接返回false;若存在,就在Map中刪除該標簽,并返回true。
對于start()方法,判斷Map的鍵中是否存在該標簽,若不存在,直接返回-1,否則可以得到標簽對應的時間段time,調用time的getStart()方法就可以得到時間段的開始時間并返回。
對于end()方法,同上,但要調用time的getEnd()方法得到時間段的結束時間并返回。
對于copy()方法,先新建一個空白的IntervalSet副本,然后遍歷Map。將遍歷得到的所有時間段和標簽通過insert()方法插入到新的副本中,最后返回該副本。
?????? 此外,還需要重寫toString方法提供容易閱讀的信息。
一、使用decorator裝飾器的方法進行是否允許不同的 interval 之間有重疊以及周期性的維度的個性特征的設計。向裝飾器中傳入待裝飾的IntervalSet,并在繼承該裝飾器的具體子類中實現相應的個性化功能。如下圖,在裝飾器IntervalSetDecorator中有屬性intervalSet,是一個待裝飾的IntervalSet類,而IntervalSetDecorator中的所有方法都調用intervalSet的相應方法。
①不允許重疊的IntervalSet實現如下:
首先NoOverlapIntervalSet繼承裝飾器IntervalSetDecorator,并實現IntervalSet接口。在NoOverlapIntervalSet中添加一個屬性intervalMap記錄添加的所有時間段。
在重寫的insert()方法中,首先判斷新添加的時間段是否與已添加的時間段發生重疊,如果有,直接拋出異常;否則就調用未重寫的父類的insert()方法進行插入。
?????? ②周期性的IntervalSet實現如下:
首先PeriodicIntervalSet繼承裝飾器IntervalSetDecorator,并實現IntervalSet接口。在PeriodicIntervalSet中添加一個屬性period記錄時間周期。
在重寫的insert()方法中,將開始時間和結束時間對周期取模后再調用未重寫的父類的insert()方法進行插入。
?????? 二、使用代理的方式進行是否允許時間軸上有空白這個維度的個性特征的設計。由于不允許空白相當于添加了新的方法,因此將這個特性抽象為一個接口NoBlankIntervalSet:
接口中包含三個方法:blankIntervals()返回所有的空白時間段集合;getStart-Time()返回總的開始時間;getEndTime()返回總的結束時間。
?????? 一個具體的不允許時間軸上有空白的IntervalSet只需要實現NoBlank-IntervalSet接口。具體實現類CommonNoBlankIntervalSet的實現如下:
屬性startTime是總的開始時間,endTime是總的結束時間。blankIntervals()的實現如圖,首先將空白時間段設置為整個時間段,然后遍歷所有的時間段,將這些時間段從空白時間段中去除,最后得到的就是所有的空白時間段。
由于要求必須使用 IntervalSet<L>作為其 rep 的一部分,因此選擇IntervalSet<L>組成的List作為rep。一個IntervalSet中只存儲某個標簽對應的一個時間段,如果一個標簽對應多個時間段,需要分散在不同的IntervalSet中。
對于empty()方法,直接返回一個具體實現類。
對于initial初始化方法,直接將傳入的IntervalSet的副本作為rep的一個元素。
對于insert()方法,首先判斷非法情況。之后遍歷IntervalSet列表得到標簽對應的所有時間段,判斷已存在的時間段和要增加的時間段是否存在重疊,如果重疊則拋出異常;若不重疊,就尋找某個不存在該標簽的IntervalSet,將這個時間段插入該IntervalSet,如果列表中所有的IntervalSet都包含該標簽,就需要新建一個空白的IntervaSet加入列表,再進行插入。
對于labels()方法,可以直接返回第一個IntervalSet中的所有標簽組成的集合。(因為插入時都是從頭開始遍歷的,因此不會存在某個標簽出現在后面的IntervalSet而不在第一個IntervalSet中的情況)
對于remove()方法,直接對每個IntervalSet調用remove()方法即可。
對于intervals()方法,遍歷IntervalSet列表得到標簽對應的所有時間段,將時間段從小到大進行排序(前面time的實現中進行了說明)。然后將每個時間段以它的順序作為標簽插入到一個IntervalSet中并返回。
此外,還需要重寫toString方法提供容易閱讀的信息。
與3.2.3同理,使用decorator裝飾器的方法進行是否允許不同的interval之間有重疊以及周期性的維度的個性特征的設計。向裝飾器中傳入待裝飾的MultiIntervalSet,并在繼承該裝飾器的具體子類中實現相應的個性化功能。如下圖,在裝飾器MultiIntervalSetDecorator中有屬性multiIntervalSet,是一個待裝飾的MultiIntervalSet類,而MultiIntervalSetDecorator中的所有方法都調用multiIntervalSet的相應方法。
①不允許重疊的MultiIntervalSet實現如下:
首先NoOverlapMultiIntervalSet繼承裝飾器MultiIntervalSetDecorator,并實現MultiIntervalSet接口。
在重寫的insert()方法中,首先判斷新添加的時間段是否與已添加的時間段發生重疊,如果有,直接拋出異常;否則就調用未重寫的父類的insert()方法進行插入。
②周期性的MultiIntervalSet實現如下:
首先PeriodicMultiIntervalSet繼承裝飾器MultiIntervalSetDecorator,并實現MultiIntervalSet接口。在PeriodicMultiIntervalSet中添加一個屬性period記錄時間周期。
在重寫的insert()方法中,將開始時間和結束時間對周期取模后再調用未重寫的父類的insert()方法進行插入。
設計三個應用的不同標簽,分別為“員工”(Employee)、“進程”(Process)、
“課程”(Course)。并且它們都是immutable類。
①對于Employee,具有的屬性為:姓名、職務、手機號碼。
除了相應的get方法之外,還需要重寫equals()、hashCode()和toString()方法。equals()的判斷依據是:只有三個屬性均相同時才認為是相同的。
②對于Course,具有的屬性為:課程 ID、課程名稱、教師名字、地點。
除了相應的get方法之外,還需要重寫equals()、hashCode()和toString()方法。equals()的判斷依據是:只有四個屬性均相同時才認為是相同的。
此外,Course類實現了Comparable接口,這是為了APP中顯示課程順序的合理性。
③對于Process,具有的屬性為:進程 ID、進程名稱、最短執行時間、最長執行時間。
除了相應的get方法之外,還需要重寫equals()、hashCode()和toString()方法。equals()的判斷依據是:進程ID相同時認為是相同的。
對于兩個MultiIntervalSet:s1和s2,遍歷s1中的標簽,查看s2中是否存在相同的標簽,如果不存在,則對相似度沒有貢獻;如果存在,那么這個標簽在s1和s2中各有一個時間段的集合,計算這兩個時間段集合的重合長度,將所有的重合長度加在一起除以MultiIntervalSet的時間跨度就是兩個MultiIntervalSet的相似度。
對于IntervalSet來說,由于它是一個特殊的MultiIntervalSet,因此可以把它轉換成MultiIntervalSet后再調用針對MultiIntervalSet的計算時間沖突比例的函數。
對于MultiIntervalSet,維護一個沖突時間段的集合conflictTime,初始時為空。遍歷set中的標簽,判斷該標簽和其他標簽的時間段是否存在重合,如果存在,就將沖突的時間段加入conflictTime。向conflictTime加入時間段也需要考慮重合的問題,集合中不能有重合的時間段,因此向conflictTime加入時間段時需要進行適當的合并。最后conflictTime中的時間長度除以總的時間跨度就是時間沖突比例。
對于IntervalSet來說,由于它是一個特殊的MultiIntervalSet,因此可以把它轉換成MultiIntervalSet后再調用針對MultiIntervalSet的計算空閑時間比例的函數。
對于MultiIntervalSet,原理類似于3.2.3中的blankIntervals()方法。維護一個空閑時間段的集合freeTime,初始時為整個時間段。遍歷set中的所有時間段,并將其從freeTime中刪去。最后freeTime中的時間長度除以總的時間跨度就是空閑比例。
利用上述設計和實現的ADT,實現手冊里要求的各項功能。
使用DutyIntervalSet作為數據結構,同時維護一個Employee的集合可以存儲未被安排的員工。剛進入時APP,會提示初始化一些信息,包括:排班開始日期、結束日期以及一組員工信息。初始化結束后,打印一個菜單,告訴用戶提供的一些功能。
根據用戶選擇的功能采取相應的操作。其中選項1,2涉及對Employee集合的增刪;3對應DutyIntervalSet的insert操作;4對應DutyIntervalSet的remove操作;5對應DutyIntervalSet的blankIntervals操作;7對應DutyIntervalSet的blankIntervals操作以及DutyIntervalSet的labels、start、end操作;8的操作見3.7節。由于這些操作都是簡單地使用一些函數,因此不再詳細敘述。這里只介紹“6:自動編排”的實現方法:首先調用DutyIntervalSet的blankIntervals操作得到未被安排的時間段,然后遍歷Employee集合,找出未被安排的員工,將這些未被安排的時間段和員工一對一匹配起來,并調用insert方法插入到DutyIntervalSet中。
此外,APP還擁有很好的健壯性,能面對用戶各種非法的、不符合格式的輸入。舉例來說,針對添加員工的操作,存在各種非法的輸入情況,在APP中都得到了相應的解決,并提示給用戶。
使用ProcessIntervalSet作為數據結構,同時維護一個的Map存儲進程和已執行時間的映射關系。進入APP后,會打印一個菜單,告訴用戶提供的一些功能。
根據用戶選擇的功能采取相應的操作。其中選項1,2涉及對Map的增刪;5,6對應ProcessIntervalSet的intervals操作。由于這些操作都是簡單地使用一些函數,因此不再詳細敘述。這里介紹3,4的實現方法,選項3:從時間點0開始進入一個循環,當所有的進程都被執行完成后退出循環。在循環中,首先使用隨機數(random.nextBoolean)決定是否調度進程,如果決定不調度進程,則閑置一段隨機的時間(random.nextInt);如果決定調度進程,則隨機選擇一個未完成的進程(使用隨機數選擇進程的序號)并執行一段隨機的時間(random.nextInt),執行結束后,如果該進程的總執行時間已經落到最短執行時間和最長執行時間的區間內,則該進程被執行完成,然后從這個時間點開始進行下一輪的循環。選項4:和選項3唯一的不同在于進程的選擇不是隨機的,而是選擇距離其最大執行時間差距最小的進程。
同樣地,APP擁有很好的健壯性,能面對用戶各種非法的、不符合格式的輸入。
使用CourseIntervalSet作為數據結構,同時維護一個Course的Map存儲未被安排的課程以及對應的剩余學時數。剛進入時APP,會提示初始化一些信息,包括:學期開始日期、總周數、以及一組課程信息。初始化結束后,打印一個菜單,告訴用戶提供的一些功能。
根據用戶選擇的功能采取相應的操作。其中選項1,2涉及對Course映射的增刪;3對應CourseIntervalSet的insert操作;4對應CourseIntervalSet的remove操作;5對應對Course映射的遍歷以及顯示;6,7對應API中操作的使用;8對應CourseIntervalSet的intervals操作。由于這些操作都是簡單地使用一些函數,因此不再詳細敘述。
同樣地,APP擁有很好的健壯性,能面對用戶各種非法的、不符合格式的輸入。
首先對文件的格式進行分析,文件總共包括三個部分,分別是Employee、Period和Roster,每個部分有自己獨特的格式。在假設沒有空格、縮進和空行的情況下,可以分別設計識別每個部分的正則表達式。
Employee部分的正則表達式:
Period部分的正則表達式:
Roster部分的正則表達式:
對于整個文件,由于三個部分出現的順序是不定的,即共有六種情況,因此識別整個文件的正則表達式為:
首先利用這個正則表達式判斷文件的格式是否正確,如果正確就抽取出每個部分。
利用Employee內部的格式抽取出員工信息:
利用Period的格式抽取出時間段信息:
利用Roster內部的格式抽取出排班信息:
根據抽取出的信息,就可以構造出一個排班表了,在構造的同時判斷一些錯誤,如:員工信息重復、員工未定義、時間重疊等。
在APP中,讀入用戶指定的文件,并去除所有的空格、縮進、空行,之后調用Parser解析信息,在出現錯誤時提示給用戶相應錯誤信息。
修改之前的DutyIntervalSet實現的是IntervalSet接口,但是本次變化要求每個標簽可以對應多個時間段,因此要求DutyIntervalSet實現MulitIntervalSet接口,同時還要保持不允許重疊的特征。因此只需要修改DutyIntervalSet繼承的裝飾器類型以及實現的接口類型即可。具體修改如圖:
修改前的代碼:
修改后的代碼:
由于DutyIntervalSet從實現IntervalSet接口變為實現MulitIntervalSet接口,因此一些方法會發生變化,例如不再支持start()和end()方法,同時新增加了intervals()方法。因此,在DutyRosterApp需要修改相應的實現方法。舉例來說,對于APP中的可視化排班信息的功能,需要遍歷DutyIntervalSet中的信息,之前的遍歷方式直接使用labels()方法:
但是,修改之后需要使用intervals()方法:
在其他地方也涉及這種變化,就不一一列舉了。同時,需要修改測試代碼保持測試的正確性。
除修改測試代碼的變化,一共修改大約50行代碼,花費時間較短,說明之前的設計較為合理,應對變化的能力比較強,可維護性很好。測試代碼的修改大約也在50行左右。
由于使用了裝飾器,只需要將原來傳入裝飾器的MultiIntervalSet改為不能重疊的NonOverlapMultiIntervalSet即可。如果不考慮測試代碼的話,真正修改的代碼只有一行!說明之前的設計較為合理,應對變化的能力比較強,可維護性很好。具體修改如圖:
修改前的代碼(傳入可重疊的MultiIntervalSet):
修改后的代碼(傳入不可重疊的NoOverlapMultiIntervalSet):
為了使得測試仍然保持正確,需要修改原來的測試代碼,總共修改的代碼量大約為50行,花費時間較短。
請在完成全部實驗要求之后,利用Git log指令或Git圖形化客戶端或GitHub上項目倉庫的Insight頁面,給出你的倉庫到目前為止的Object Graph,尤其是區分清楚change分支和master分支所指向的位置。
使用git log指令, 得到如下結果:
可以看出,Git倉庫到目前為止的Object Graph有如下形式:
請使用表格方式記錄你的進度情況,以超過半小時的連續編程時間為一行。
每次結束編程時,請向該表格中增加一行。不要事后胡亂填寫。
不要嫌煩,該表格可幫助你匯總你在每個任務上付出的時間和精力,發現自己不擅長的任務,后續有意識的彌補。
| 日期 | 時間段 | 計劃任務 | 實際完成情況 |
| 2021.6.28 | 12:30-14:30 | 設計IntervalSet接口并完成一個具體的實現類CommonIntervalSet,編寫測試 | 完成 |
| 2021.6.28 | 14:30-17:00 | 設計MultiIntervalSet接口并完成具體的實現類CommonMultiIntervalSet,編寫測試 | 完成 |
| 2021.6.28 | 17:30-18:30 | 設計IntervalSet和Common-MultiIntervalSet的裝飾器 | 完成 |
| 2021.6.28 | 18:30-19:30 | 在有無空白的維度上,設計NoBlankIntervalSet接口并完成具體實現類CommonNoBlankIntervalSet,編寫測試 | 完成 |
| 2021.6.28 | 19:30-21:00 | 在是否允許重疊的維度上,設計實現不允許重疊的裝飾類NoOverlapIntervalSet和NoOverlap-MultiIntervalSet,編寫測試 | 完成 |
| 2021.6.28 | 21:30-22:30 | 在周期性的維度上,設計實現周期性的裝飾類PeriodicIntervalSet和PeriodicMultiIntervalSet,編寫測試 | 完成 |
| 2021.6.29 | 9:00-10:00 | 實現Employee、Process、Course類,編寫測試 | 完成 |
| 2021.6.29 | 10:00-12:30 | 設計實現API,編寫測試 | 完成 |
| 2021.6.29 | 13:30-14:00 | 實現DutyIntervalSet、Process-IntervalSet和CourseIntervalSet,編寫測試 | 完成 |
| 2021.6.29 | 14:00-17:30 | 實現DutyRosterApp | 測試健壯性時發現很多不足,延期一小時完成 |
| 2021.6.29 | 19:00-22:00 | 實現CourseScheduleApp | 測試健壯性時發現很多不足,延期半小時完成 |
| 2021.6.30 | 9:00-12:00 | 實現ProcessScheduleApp | 完成 |
| 2021.6.30 | 16:30-17:30 | 學習正則表達式 | 完成 |
| 2021.6.30 | 18:30-20:30 | 設計實現解析器Parser,編寫測試 | 由于對正則表達式不太熟悉,延期半小時完成 |
| 2021.6.30 | 21:00-22:30 | 向DutyRosterApp中加入解析文件功能 | 完成 |
| 2021.7.1 | 9:00-10:00 | 完善整個項目的注釋(spec、AF、RI、safe from exposure、test strategy) | 完成 |
| 2021.7.1 | 10:30-11:30 | 修改代碼以面對新的變化 | 完成 |
| 遇到的難點 | 解決途徑 |
| 對裝飾器不夠熟悉,不知如何編寫正確的裝飾器以實現功能。 | 在網上查看其它應用使用裝飾器的方法并加以總結,逐漸熟悉裝飾器的使用。 |
| 編寫APP時遇到很多的健壯性問題。 | 仔細分析每個步驟中用戶可能的所有輸入,針對任何非法情況都作出提示。雖然很耗費時間,但效果很好。 |
| 不知如何設計正則表達式來抽取大量的文件信息。 | 在學習正則表達式語法的同時進行一些小的測試,從小的正則表達式開始,逐漸累積成復雜的正則表達式,最終實現對文件的解析。 |
設計ADT時一定要考慮得全面且清楚,在最終決定實現方案后再編寫代碼,否則在后面的應用實現中發現問題時只能再重新設計。同時,在編寫APP時,一定要事先考慮健壯性的問題,不然,在編寫完代碼之后進行測試時會遇到很多的健壯性問題,這時再去修改就會使得代碼很臃腫,可讀性變差,出錯的可能性更高。
面向ADT的編程需要從實際中進行抽象,并進行合理的設計,有很好的可擴展性和可復用性;而直接面向應用場景編程只針對特定應用,每次更換應用場景時都要重新編程,擴展性很差,只適合簡單的應用場景。本實驗中設計一個ADT就可以應用到三個不同的場景,大大縮短了開發時間。
這些工作使得客戶端了解各方法的功能但無法得知內部具體實現,可以防止內部變量被客戶端惡意修改,時刻檢查表示不變量,保證安全性。雖然這些工作有些麻煩,但卻是好的軟件必須具備的,因此我愿意在以后編程中堅持這么做。同時,在復雜的軟件開發過程中,好的注釋可以節省大量閱讀代碼的時間,使得開發時間大大降低。
由于API面向的場景是廣泛的,因此開發難度很大,但是一旦開發一個好的API,就可以在大量場合中應用,大大提高代碼的復用性。
語法驅動編程為實際應用提供了很大的便利,用戶無需繁瑣地一行一行地輸入信息,而只需提供一個文件以及一定的語法規則即可。而對于應用的開發者,只需根據語法規則編寫代碼,即使改變語法規則也可以很快地修改實現代碼,有很好的可維護性。
ADT的難度主要體現在抽象上。一個好的ADT既不能過于具體,也不能過于抽象。需要從大量應用場景中尋找共性,抽象的程度也很難把握。對于這種情況,只能反復的推敲,比較不同設計方案的差異并選擇最好的ADT設計方案。
接口、抽象類、類的抽象程度一定是逐漸降低的,將所有應用的共性抽象為接口,然后在抽象類以及類中添加新的特性。通過接口的組合可以形成新的接口,并可以具備不同接口中的抽象。類的繼承也可以增加更具體的新的特性。同時,使用正確的設計模式可以使得代碼的可復用性和可維護性最大化。
難度適中,可以接受,但是工作量很大,尤其是APP的編寫要花費很長時間。雖然給了三周時間,但是和其他課的實驗、大作業、考試有重疊,總體上時間還是很緊。希望各課程的老師可以相互協調一下實驗安排。
逐漸理解了軟件構造過程中獨有的思路和方法,也逐漸適應了與之前完全不同的編程過程。通過Lab3大量的代碼訓練,自己的編程水平也有了極大的提高。
總結
以上是生活随笔為你收集整理的2021哈工大软件构造Lab3的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 全球与中国键槽拉刀市场深度研究分析报告
- 下一篇: 微信内网页分享,分享者能看到分享的图片(