日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

2021哈工大软件构造Lab3

發(fā)布時(shí)間:2023/12/20 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 2021哈工大软件构造Lab3 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

2021年春季學(xué)期
計(jì)算學(xué)部《軟件構(gòu)造》課程

Lab 3實(shí)驗(yàn)報(bào)告

實(shí)驗(yàn)源碼:https://github.com/1190200817/SC_Lab3

目錄

1 實(shí)驗(yàn)?zāi)繕?biāo)概述··· 1

2 實(shí)驗(yàn)環(huán)境配置··· 1

3 實(shí)驗(yàn)過程··· 1

3.1 待開發(fā)的三個(gè)應(yīng)用場景··· 1

3.2 面向可復(fù)用性和可維護(hù)性的設(shè)計(jì):IntervalSet<L>· 2

3.2.1 IntervalSet<L>的共性操作··· 2

3.2.2 局部共性特征的設(shè)計(jì)方案··· 3

3.2.3 面向各應(yīng)用的IntervalSet子類型設(shè)計(jì)(個(gè)性化特征的設(shè)計(jì)方案)··· 4

3.3 面向可復(fù)用性和可維護(hù)性的設(shè)計(jì):MultiIntervalSet<L>· 7

3.3.1 MultiIntervalSet<L>的共性操作··· 7

3.3.2 局部共性特征的設(shè)計(jì)方案··· 7

3.3.3 面向各應(yīng)用的MultiIntervalSet子類型設(shè)計(jì)(個(gè)性化特征的設(shè)計(jì)方案)··· 9

3.4 面向復(fù)用的設(shè)計(jì):L· 11

3.5 可復(fù)用API設(shè)計(jì)··· 13

3.5.1 計(jì)算相似度··· 13

3.5.2 計(jì)算時(shí)間沖突比例··· 14

3.5.3 計(jì)算空閑時(shí)間比例··· 15

3.6 應(yīng)用設(shè)計(jì)與開發(fā)··· 15

3.6.1 排班管理系統(tǒng)··· 15

3.6.2 操作系統(tǒng)的進(jìn)程調(diào)度管理系統(tǒng)··· 17

3.6.3 課表管理系統(tǒng)··· 18

3.7 基于語法的數(shù)據(jù)讀入··· 18

3.8 應(yīng)對(duì)面臨的新變化··· 20

3.8.1 變化1· 20

3.8.2 變化2· 21

3.9 Git倉庫結(jié)構(gòu)··· 21

4 實(shí)驗(yàn)進(jìn)度記錄··· 22

5 實(shí)驗(yàn)過程中遇到的困難與解決途徑··· 23

6 實(shí)驗(yàn)過程中收獲的經(jīng)驗(yàn)、教訓(xùn)、感想··· 24

6.1 實(shí)驗(yàn)過程中收獲的經(jīng)驗(yàn)和教訓(xùn)··· 24

6.2 針對(duì)以下方面的感受··· 24

  • 實(shí)驗(yàn)?zāi)繕?biāo)概述
  • 本次實(shí)驗(yàn)覆蓋課程第 2、3 章的內(nèi)容,目標(biāo)是編寫具有可復(fù)用性和可維護(hù)性的軟件,主要使用以下軟件構(gòu)造技術(shù):

  • 子類型、泛型、多態(tài)、重寫、重載
  • 繼承、代理、組合
  • 語法驅(qū)動(dòng)的編程、正則表達(dá)式
  • API 設(shè)計(jì)、API 復(fù)用
  • 本次實(shí)驗(yàn)給定了三個(gè)具體應(yīng)用(值班表管理、操作系統(tǒng)進(jìn)程調(diào)度管理、大學(xué)課表管理),學(xué)生不是直接針對(duì)每個(gè)應(yīng)用分別編程實(shí)現(xiàn),而是通過 ADT 和泛型等抽象技術(shù),開發(fā)一套可復(fù)用的 ADT 及其實(shí)現(xiàn),充分考慮這些應(yīng)用之間的相似性和差異性,使 ADT 有更大程度的復(fù)用(可復(fù)用性)和更容易面向各種變化(可維護(hù)性)。

  • 實(shí)驗(yàn)環(huán)境配置
  • Eclipse和Git在上一次實(shí)驗(yàn)中已經(jīng)配置好了。本次實(shí)驗(yàn)無需額外配置環(huán)境。

    在這里給出你的GitHub Lab3倉庫的URL地址(Lab3-學(xué)號(hào))。

    https://github.com/ComputerScienceHIT/HIT-Lab3-1190200817

  • 實(shí)驗(yàn)過程
  • 請(qǐng)仔細(xì)對(duì)照實(shí)驗(yàn)手冊(cè),針對(duì)每一項(xiàng)任務(wù),在下面各節(jié)中記錄你的實(shí)驗(yàn)過程、闡述你的設(shè)計(jì)思路和問題求解思路,可輔之以示意圖或關(guān)鍵源代碼加以說明(但千萬不要把你的源代碼全部粘貼過來!)。

  • 待開發(fā)的三個(gè)應(yīng)用場景
  • 簡要介紹三個(gè)應(yīng)用:

    ①值班表管理,一個(gè)單位有 n 個(gè)員工,在某個(gè)時(shí)間段內(nèi)安排值班。每天只能安排一個(gè)員工且不能出現(xiàn)無人值班的情況;每個(gè)員工需要安排在連續(xù)的幾天內(nèi)。值班表內(nèi)需要記錄員工的名字、職位、手機(jī)號(hào)碼,以便于外界聯(lián)系值班員。

    ②操作系統(tǒng)進(jìn)程調(diào)度管理,進(jìn)程被調(diào)度在 CPU 上執(zhí)行,操作系統(tǒng)決定在各個(gè)時(shí)段內(nèi)執(zhí)行哪個(gè)進(jìn)程。操作系統(tǒng)可掛起某個(gè)正在執(zhí)行的進(jìn)程,在后續(xù)時(shí)刻可以恢復(fù)執(zhí)行被掛起的進(jìn)程。每個(gè)時(shí)間只能有一個(gè)進(jìn)程在執(zhí)行,其他進(jìn)程處于休眠狀態(tài);一個(gè)進(jìn)程的執(zhí)行被分為多個(gè)時(shí)間段;在特定時(shí)刻,CPU 可以“閑置”;調(diào)度無規(guī)律,可看作是隨機(jī)調(diào)度。

    ③大學(xué)課表管理:課程需要特定的教室和特定的教師。假設(shè)各周的課表都是完全一樣的,同樣的課程安排將以“周”為單位進(jìn)行周期性的重復(fù),直到學(xué)期結(jié)束;一門課程每周可以出現(xiàn) 1 次,也可以安排多次,且由同一位教師承擔(dān)并在同樣的教室進(jìn)行;允許課表中有空白時(shí)間段;同一個(gè)時(shí)間段內(nèi)可以安排不同的課程;一位教師也可以承擔(dān)課表中的多門課程。

    相同之處:都包含了具有不同特征的“時(shí)間段集合”對(duì)象。每個(gè)時(shí)間段對(duì)應(yīng)一個(gè)對(duì)象標(biāo)簽。

    不同之處:有三個(gè)維度上的差異。①是否允許時(shí)間軸上有空白。在應(yīng)用1中,不允許有空白;在應(yīng)用2和應(yīng)用3中,允許有空白。②是否允許不同的 interval 之間有重疊。在應(yīng)用1和應(yīng)用2中,不允許有重疊;在應(yīng)用3中,允許有重疊。③是否包含周期性的時(shí)間段。應(yīng)用 3 中以“一周”為單位重復(fù)某個(gè)課程,但應(yīng)用1和應(yīng)用2中不存在這種情況。

  • 面向可復(fù)用性和可維護(hù)性的設(shè)計(jì):IntervalSet<L>
  • 該節(jié)是本實(shí)驗(yàn)的核心部分。

  • IntervalSet<L>的共性操作
  • 這個(gè)ADT描述了一組在時(shí)間軸上分布的“時(shí)間段”,每個(gè)時(shí)間段附著一個(gè)特定的標(biāo)簽,且標(biāo)簽不重復(fù)。因此共性的方法包括:

  • 靜態(tài)工廠方法empty():創(chuàng)建一個(gè)空對(duì)象。
  • void insert(long start, long end, L label):在當(dāng)前對(duì)象中插入新的時(shí)間段和標(biāo)簽。
  • Set<L> labels():獲得當(dāng)前對(duì)象中的標(biāo)簽集合。
  • boolean remove(L label):從當(dāng)前對(duì)象中移除某個(gè)標(biāo)簽所關(guān)聯(lián)的時(shí)間段。
  • long start (L label):返回某個(gè)標(biāo)簽對(duì)應(yīng)的時(shí)間段的開始時(shí)間。
  • long end (L label):返回某個(gè)標(biāo)簽對(duì)應(yīng)的時(shí)間段的結(jié)束時(shí)間。
  • IntervalSet<L> copy():返回這個(gè)對(duì)象的副本。
  • 局部共性特征的設(shè)計(jì)方案
  • 由于IntervalSet是一對(duì)一的結(jié)構(gòu),即一個(gè)標(biāo)簽對(duì)應(yīng)一個(gè)時(shí)間段,因此可以使用Map作為內(nèi)部的數(shù)據(jù)結(jié)構(gòu),定義為:

    private final Map<L, time> intervalMap = new HashMap<>();

    由于時(shí)間段是兩個(gè)long型的整數(shù)構(gòu)成的,因此可以定義一個(gè)輔助類time表示一個(gè)時(shí)間段。其中start是時(shí)間段的開始,end是時(shí)間段的結(jié)束,同時(shí)要求start一定大于0且start一定比end小。這里time實(shí)現(xiàn)了Comparable接口是為了滿足下面MultiIntervalSet中的一些功能。

    對(duì)于empty()方法,直接返回一個(gè)具體實(shí)現(xiàn)類。

    對(duì)于insert()方法,首先判斷非法條件(start<0和start>=end),然后判斷Map中是否包含標(biāo)簽,若包含該標(biāo)簽,那只有在重復(fù)設(shè)置相同時(shí)間段時(shí)才合法,否則會(huì)拋出IntervalConflictException;若不包含該標(biāo)簽,就直接插入時(shí)間段。(注意,這里是允許不同標(biāo)簽之間存在Overlap的)

    對(duì)于labels()方法,直接返回Map中所有的鍵構(gòu)成的集合即可。

    對(duì)于remove()方法,判斷Map的鍵中是否存在該標(biāo)簽,若不存在,直接返回false;若存在,就在Map中刪除該標(biāo)簽,并返回true。

    對(duì)于start()方法,判斷Map的鍵中是否存在該標(biāo)簽,若不存在,直接返回-1,否則可以得到標(biāo)簽對(duì)應(yīng)的時(shí)間段time,調(diào)用time的getStart()方法就可以得到時(shí)間段的開始時(shí)間并返回。

    對(duì)于end()方法,同上,但要調(diào)用time的getEnd()方法得到時(shí)間段的結(jié)束時(shí)間并返回。

    對(duì)于copy()方法,先新建一個(gè)空白的IntervalSet副本,然后遍歷Map。將遍歷得到的所有時(shí)間段和標(biāo)簽通過insert()方法插入到新的副本中,最后返回該副本。

    ?????? 此外,還需要重寫toString方法提供容易閱讀的信息。

  • 面向各應(yīng)用的IntervalSet子類型設(shè)計(jì)(個(gè)性化特征的設(shè)計(jì)方案)
  • 一、使用decorator裝飾器的方法進(jìn)行是否允許不同的 interval 之間有重疊以及周期性的維度的個(gè)性特征的設(shè)計(jì)。向裝飾器中傳入待裝飾的IntervalSet,并在繼承該裝飾器的具體子類中實(shí)現(xiàn)相應(yīng)的個(gè)性化功能。如下圖,在裝飾器IntervalSetDecorator中有屬性intervalSet,是一個(gè)待裝飾的IntervalSet類,而IntervalSetDecorator中的所有方法都調(diào)用intervalSet的相應(yīng)方法。

    ①不允許重疊的IntervalSet實(shí)現(xiàn)如下:

    首先NoOverlapIntervalSet繼承裝飾器IntervalSetDecorator,并實(shí)現(xiàn)IntervalSet接口。在NoOverlapIntervalSet中添加一個(gè)屬性intervalMap記錄添加的所有時(shí)間段。

    在重寫的insert()方法中,首先判斷新添加的時(shí)間段是否與已添加的時(shí)間段發(fā)生重疊,如果有,直接拋出異常;否則就調(diào)用未重寫的父類的insert()方法進(jìn)行插入。

    ?????? ②周期性的IntervalSet實(shí)現(xiàn)如下:

    首先PeriodicIntervalSet繼承裝飾器IntervalSetDecorator,并實(shí)現(xiàn)IntervalSet接口。在PeriodicIntervalSet中添加一個(gè)屬性period記錄時(shí)間周期。

    在重寫的insert()方法中,將開始時(shí)間和結(jié)束時(shí)間對(duì)周期取模后再調(diào)用未重寫的父類的insert()方法進(jìn)行插入。

    ?????? 二、使用代理的方式進(jìn)行是否允許時(shí)間軸上有空白這個(gè)維度的個(gè)性特征的設(shè)計(jì)。由于不允許空白相當(dāng)于添加了新的方法,因此將這個(gè)特性抽象為一個(gè)接口NoBlankIntervalSet:

    接口中包含三個(gè)方法:blankIntervals()返回所有的空白時(shí)間段集合;getStart-Time()返回總的開始時(shí)間;getEndTime()返回總的結(jié)束時(shí)間。

    ?????? 一個(gè)具體的不允許時(shí)間軸上有空白的IntervalSet只需要實(shí)現(xiàn)NoBlank-IntervalSet接口。具體實(shí)現(xiàn)類CommonNoBlankIntervalSet的實(shí)現(xiàn)如下:

    屬性startTime是總的開始時(shí)間,endTime是總的結(jié)束時(shí)間。blankIntervals()的實(shí)現(xiàn)如圖,首先將空白時(shí)間段設(shè)置為整個(gè)時(shí)間段,然后遍歷所有的時(shí)間段,將這些時(shí)間段從空白時(shí)間段中去除,最后得到的就是所有的空白時(shí)間段。

  • 面向可復(fù)用性和可維護(hù)性的設(shè)計(jì):MultiIntervalSet<L>
  • MultiIntervalSet<L>的共性操作
  • 靜態(tài)工廠方法empty():創(chuàng)建一個(gè)空對(duì)象。
  • MultiIntervalSet(IntervalSet<L> initial):利用initial中包含的數(shù)據(jù)創(chuàng)建非空對(duì)象。
  • void insert(long start, long end, L label):在當(dāng)前對(duì)象中插入新的時(shí)間段和標(biāo)簽。
  • Set<L> labels():獲得當(dāng)前對(duì)象中的標(biāo)簽集合。
  • boolean remove(L label):從當(dāng)前對(duì)象中移除某個(gè)標(biāo)簽所關(guān)聯(lián)的所有時(shí)間段。
  • IntervalSet<Integer> intervals(L label):從當(dāng)前對(duì)象中獲取與某個(gè)標(biāo)簽所關(guān)聯(lián)的所有時(shí)間段。
  • 局部共性特征的設(shè)計(jì)方案
  • 由于要求必須使用 IntervalSet<L>作為其 rep 的一部分,因此選擇IntervalSet<L>組成的List作為rep。一個(gè)IntervalSet中只存儲(chǔ)某個(gè)標(biāo)簽對(duì)應(yīng)的一個(gè)時(shí)間段,如果一個(gè)標(biāo)簽對(duì)應(yīng)多個(gè)時(shí)間段,需要分散在不同的IntervalSet中。

    對(duì)于empty()方法,直接返回一個(gè)具體實(shí)現(xiàn)類。

    對(duì)于initial初始化方法,直接將傳入的IntervalSet的副本作為rep的一個(gè)元素。

    對(duì)于insert()方法,首先判斷非法情況。之后遍歷IntervalSet列表得到標(biāo)簽對(duì)應(yīng)的所有時(shí)間段,判斷已存在的時(shí)間段和要增加的時(shí)間段是否存在重疊,如果重疊則拋出異常;若不重疊,就尋找某個(gè)不存在該標(biāo)簽的IntervalSet,將這個(gè)時(shí)間段插入該IntervalSet,如果列表中所有的IntervalSet都包含該標(biāo)簽,就需要新建一個(gè)空白的IntervaSet加入列表,再進(jìn)行插入。

    對(duì)于labels()方法,可以直接返回第一個(gè)IntervalSet中的所有標(biāo)簽組成的集合。(因?yàn)椴迦霑r(shí)都是從頭開始遍歷的,因此不會(huì)存在某個(gè)標(biāo)簽出現(xiàn)在后面的IntervalSet而不在第一個(gè)IntervalSet中的情況)

    對(duì)于remove()方法,直接對(duì)每個(gè)IntervalSet調(diào)用remove()方法即可。

    對(duì)于intervals()方法,遍歷IntervalSet列表得到標(biāo)簽對(duì)應(yīng)的所有時(shí)間段,將時(shí)間段從小到大進(jìn)行排序(前面time的實(shí)現(xiàn)中進(jìn)行了說明)。然后將每個(gè)時(shí)間段以它的順序作為標(biāo)簽插入到一個(gè)IntervalSet中并返回。

    此外,還需要重寫toString方法提供容易閱讀的信息。

  • 面向各應(yīng)用的MultiIntervalSet子類型設(shè)計(jì)(個(gè)性化特征的設(shè)計(jì)方案)
  • 與3.2.3同理,使用decorator裝飾器的方法進(jìn)行是否允許不同的interval之間有重疊以及周期性的維度的個(gè)性特征的設(shè)計(jì)。向裝飾器中傳入待裝飾的MultiIntervalSet,并在繼承該裝飾器的具體子類中實(shí)現(xiàn)相應(yīng)的個(gè)性化功能。如下圖,在裝飾器MultiIntervalSetDecorator中有屬性multiIntervalSet,是一個(gè)待裝飾的MultiIntervalSet類,而MultiIntervalSetDecorator中的所有方法都調(diào)用multiIntervalSet的相應(yīng)方法。

    ①不允許重疊的MultiIntervalSet實(shí)現(xiàn)如下:

    首先NoOverlapMultiIntervalSet繼承裝飾器MultiIntervalSetDecorator,并實(shí)現(xiàn)MultiIntervalSet接口。

    在重寫的insert()方法中,首先判斷新添加的時(shí)間段是否與已添加的時(shí)間段發(fā)生重疊,如果有,直接拋出異常;否則就調(diào)用未重寫的父類的insert()方法進(jìn)行插入。

    ②周期性的MultiIntervalSet實(shí)現(xiàn)如下:

    首先PeriodicMultiIntervalSet繼承裝飾器MultiIntervalSetDecorator,并實(shí)現(xiàn)MultiIntervalSet接口。在PeriodicMultiIntervalSet中添加一個(gè)屬性period記錄時(shí)間周期。

    在重寫的insert()方法中,將開始時(shí)間和結(jié)束時(shí)間對(duì)周期取模后再調(diào)用未重寫的父類的insert()方法進(jìn)行插入。

  • 面向復(fù)用的設(shè)計(jì):L
  • 設(shè)計(jì)三個(gè)應(yīng)用的不同標(biāo)簽,分別為“員工”(Employee)、“進(jìn)程”(Process)、

    “課程”(Course)。并且它們都是immutable類。

    ①對(duì)于Employee,具有的屬性為:姓名、職務(wù)、手機(jī)號(hào)碼。

    除了相應(yīng)的get方法之外,還需要重寫equals()、hashCode()和toString()方法。equals()的判斷依據(jù)是:只有三個(gè)屬性均相同時(shí)才認(rèn)為是相同的。

    ②對(duì)于Course,具有的屬性為:課程 ID、課程名稱、教師名字、地點(diǎn)。

    除了相應(yīng)的get方法之外,還需要重寫equals()、hashCode()和toString()方法。equals()的判斷依據(jù)是:只有四個(gè)屬性均相同時(shí)才認(rèn)為是相同的。

    此外,Course類實(shí)現(xiàn)了Comparable接口,這是為了APP中顯示課程順序的合理性。

    ③對(duì)于Process,具有的屬性為:進(jìn)程 ID、進(jìn)程名稱、最短執(zhí)行時(shí)間、最長執(zhí)行時(shí)間。

    除了相應(yīng)的get方法之外,還需要重寫equals()、hashCode()和toString()方法。equals()的判斷依據(jù)是:進(jìn)程ID相同時(shí)認(rèn)為是相同的。

  • 可復(fù)用API設(shè)計(jì)
  • 計(jì)算相似度
  • 對(duì)于兩個(gè)MultiIntervalSet:s1和s2,遍歷s1中的標(biāo)簽,查看s2中是否存在相同的標(biāo)簽,如果不存在,則對(duì)相似度沒有貢獻(xiàn);如果存在,那么這個(gè)標(biāo)簽在s1和s2中各有一個(gè)時(shí)間段的集合,計(jì)算這兩個(gè)時(shí)間段集合的重合長度,將所有的重合長度加在一起除以MultiIntervalSet的時(shí)間跨度就是兩個(gè)MultiIntervalSet的相似度。

  • 計(jì)算時(shí)間沖突比例
  • 對(duì)于IntervalSet來說,由于它是一個(gè)特殊的MultiIntervalSet,因此可以把它轉(zhuǎn)換成MultiIntervalSet后再調(diào)用針對(duì)MultiIntervalSet的計(jì)算時(shí)間沖突比例的函數(shù)。

    對(duì)于MultiIntervalSet,維護(hù)一個(gè)沖突時(shí)間段的集合conflictTime,初始時(shí)為空。遍歷set中的標(biāo)簽,判斷該標(biāo)簽和其他標(biāo)簽的時(shí)間段是否存在重合,如果存在,就將沖突的時(shí)間段加入conflictTime。向conflictTime加入時(shí)間段也需要考慮重合的問題,集合中不能有重合的時(shí)間段,因此向conflictTime加入時(shí)間段時(shí)需要進(jìn)行適當(dāng)?shù)暮喜ⅰW詈骳onflictTime中的時(shí)間長度除以總的時(shí)間跨度就是時(shí)間沖突比例。

  • 計(jì)算空閑時(shí)間比例
  • 對(duì)于IntervalSet來說,由于它是一個(gè)特殊的MultiIntervalSet,因此可以把它轉(zhuǎn)換成MultiIntervalSet后再調(diào)用針對(duì)MultiIntervalSet的計(jì)算空閑時(shí)間比例的函數(shù)。

    對(duì)于MultiIntervalSet,原理類似于3.2.3中的blankIntervals()方法。維護(hù)一個(gè)空閑時(shí)間段的集合freeTime,初始時(shí)為整個(gè)時(shí)間段。遍歷set中的所有時(shí)間段,并將其從freeTime中刪去。最后freeTime中的時(shí)間長度除以總的時(shí)間跨度就是空閑比例。

  • 應(yīng)用設(shè)計(jì)與開發(fā)
  • 利用上述設(shè)計(jì)和實(shí)現(xiàn)的ADT,實(shí)現(xiàn)手冊(cè)里要求的各項(xiàng)功能。

  • 排班管理系統(tǒng)
  • 使用DutyIntervalSet作為數(shù)據(jù)結(jié)構(gòu),同時(shí)維護(hù)一個(gè)Employee的集合可以存儲(chǔ)未被安排的員工。剛進(jìn)入時(shí)APP,會(huì)提示初始化一些信息,包括:排班開始日期、結(jié)束日期以及一組員工信息。初始化結(jié)束后,打印一個(gè)菜單,告訴用戶提供的一些功能。

    根據(jù)用戶選擇的功能采取相應(yīng)的操作。其中選項(xiàng)1,2涉及對(duì)Employee集合的增刪;3對(duì)應(yīng)DutyIntervalSet的insert操作;4對(duì)應(yīng)DutyIntervalSet的remove操作;5對(duì)應(yīng)DutyIntervalSet的blankIntervals操作;7對(duì)應(yīng)DutyIntervalSet的blankIntervals操作以及DutyIntervalSet的labels、start、end操作;8的操作見3.7節(jié)。由于這些操作都是簡單地使用一些函數(shù),因此不再詳細(xì)敘述。這里只介紹“6:自動(dòng)編排”的實(shí)現(xiàn)方法:首先調(diào)用DutyIntervalSet的blankIntervals操作得到未被安排的時(shí)間段,然后遍歷Employee集合,找出未被安排的員工,將這些未被安排的時(shí)間段和員工一對(duì)一匹配起來,并調(diào)用insert方法插入到DutyIntervalSet中。

    此外,APP還擁有很好的健壯性,能面對(duì)用戶各種非法的、不符合格式的輸入。舉例來說,針對(duì)添加員工的操作,存在各種非法的輸入情況,在APP中都得到了相應(yīng)的解決,并提示給用戶。

  • 操作系統(tǒng)的進(jìn)程調(diào)度管理系統(tǒng)
  • 使用ProcessIntervalSet作為數(shù)據(jù)結(jié)構(gòu),同時(shí)維護(hù)一個(gè)的Map存儲(chǔ)進(jìn)程和已執(zhí)行時(shí)間的映射關(guān)系。進(jìn)入APP后,會(huì)打印一個(gè)菜單,告訴用戶提供的一些功能。

    根據(jù)用戶選擇的功能采取相應(yīng)的操作。其中選項(xiàng)1,2涉及對(duì)Map的增刪;5,6對(duì)應(yīng)ProcessIntervalSet的intervals操作。由于這些操作都是簡單地使用一些函數(shù),因此不再詳細(xì)敘述。這里介紹3,4的實(shí)現(xiàn)方法,選項(xiàng)3:從時(shí)間點(diǎn)0開始進(jìn)入一個(gè)循環(huán),當(dāng)所有的進(jìn)程都被執(zhí)行完成后退出循環(huán)。在循環(huán)中,首先使用隨機(jī)數(shù)(random.nextBoolean)決定是否調(diào)度進(jìn)程,如果決定不調(diào)度進(jìn)程,則閑置一段隨機(jī)的時(shí)間(random.nextInt);如果決定調(diào)度進(jìn)程,則隨機(jī)選擇一個(gè)未完成的進(jìn)程(使用隨機(jī)數(shù)選擇進(jìn)程的序號(hào))并執(zhí)行一段隨機(jī)的時(shí)間(random.nextInt),執(zhí)行結(jié)束后,如果該進(jìn)程的總執(zhí)行時(shí)間已經(jīng)落到最短執(zhí)行時(shí)間和最長執(zhí)行時(shí)間的區(qū)間內(nèi),則該進(jìn)程被執(zhí)行完成,然后從這個(gè)時(shí)間點(diǎn)開始進(jìn)行下一輪的循環(huán)。選項(xiàng)4:和選項(xiàng)3唯一的不同在于進(jìn)程的選擇不是隨機(jī)的,而是選擇距離其最大執(zhí)行時(shí)間差距最小的進(jìn)程。

    同樣地,APP擁有很好的健壯性,能面對(duì)用戶各種非法的、不符合格式的輸入。

  • 課表管理系統(tǒng)
  • 使用CourseIntervalSet作為數(shù)據(jù)結(jié)構(gòu),同時(shí)維護(hù)一個(gè)Course的Map存儲(chǔ)未被安排的課程以及對(duì)應(yīng)的剩余學(xué)時(shí)數(shù)。剛進(jìn)入時(shí)APP,會(huì)提示初始化一些信息,包括:學(xué)期開始日期、總周數(shù)、以及一組課程信息。初始化結(jié)束后,打印一個(gè)菜單,告訴用戶提供的一些功能。

    根據(jù)用戶選擇的功能采取相應(yīng)的操作。其中選項(xiàng)1,2涉及對(duì)Course映射的增刪;3對(duì)應(yīng)CourseIntervalSet的insert操作;4對(duì)應(yīng)CourseIntervalSet的remove操作;5對(duì)應(yīng)對(duì)Course映射的遍歷以及顯示;6,7對(duì)應(yīng)API中操作的使用;8對(duì)應(yīng)CourseIntervalSet的intervals操作。由于這些操作都是簡單地使用一些函數(shù),因此不再詳細(xì)敘述。

    同樣地,APP擁有很好的健壯性,能面對(duì)用戶各種非法的、不符合格式的輸入。

  • 基于語法的數(shù)據(jù)讀入
  • 首先對(duì)文件的格式進(jìn)行分析,文件總共包括三個(gè)部分,分別是Employee、Period和Roster,每個(gè)部分有自己獨(dú)特的格式。在假設(shè)沒有空格、縮進(jìn)和空行的情況下,可以分別設(shè)計(jì)識(shí)別每個(gè)部分的正則表達(dá)式。

    Employee部分的正則表達(dá)式:

    Period部分的正則表達(dá)式:

    Roster部分的正則表達(dá)式:

    對(duì)于整個(gè)文件,由于三個(gè)部分出現(xiàn)的順序是不定的,即共有六種情況,因此識(shí)別整個(gè)文件的正則表達(dá)式為:

    首先利用這個(gè)正則表達(dá)式判斷文件的格式是否正確,如果正確就抽取出每個(gè)部分。

    利用Employee內(nèi)部的格式抽取出員工信息:

    利用Period的格式抽取出時(shí)間段信息:

    利用Roster內(nèi)部的格式抽取出排班信息:

    根據(jù)抽取出的信息,就可以構(gòu)造出一個(gè)排班表了,在構(gòu)造的同時(shí)判斷一些錯(cuò)誤,如:員工信息重復(fù)、員工未定義、時(shí)間重疊等。

    在APP中,讀入用戶指定的文件,并去除所有的空格、縮進(jìn)、空行,之后調(diào)用Parser解析信息,在出現(xiàn)錯(cuò)誤時(shí)提示給用戶相應(yīng)錯(cuò)誤信息。

  • 應(yīng)對(duì)面臨的新變化
  • 變化1
  • 修改之前的DutyIntervalSet實(shí)現(xiàn)的是IntervalSet接口,但是本次變化要求每個(gè)標(biāo)簽可以對(duì)應(yīng)多個(gè)時(shí)間段,因此要求DutyIntervalSet實(shí)現(xiàn)MulitIntervalSet接口,同時(shí)還要保持不允許重疊的特征。因此只需要修改DutyIntervalSet繼承的裝飾器類型以及實(shí)現(xiàn)的接口類型即可。具體修改如圖:

    修改前的代碼:

    修改后的代碼:

    由于DutyIntervalSet從實(shí)現(xiàn)IntervalSet接口變?yōu)閷?shí)現(xiàn)MulitIntervalSet接口,因此一些方法會(huì)發(fā)生變化,例如不再支持start()和end()方法,同時(shí)新增加了intervals()方法。因此,在DutyRosterApp需要修改相應(yīng)的實(shí)現(xiàn)方法。舉例來說,對(duì)于APP中的可視化排班信息的功能,需要遍歷DutyIntervalSet中的信息,之前的遍歷方式直接使用labels()方法:

    但是,修改之后需要使用intervals()方法:

    在其他地方也涉及這種變化,就不一一列舉了。同時(shí),需要修改測試代碼保持測試的正確性。

    除修改測試代碼的變化,一共修改大約50行代碼,花費(fèi)時(shí)間較短,說明之前的設(shè)計(jì)較為合理,應(yīng)對(duì)變化的能力比較強(qiáng),可維護(hù)性很好。測試代碼的修改大約也在50行左右。

  • 變化2
  • 由于使用了裝飾器,只需要將原來傳入裝飾器的MultiIntervalSet改為不能重疊的NonOverlapMultiIntervalSet即可。如果不考慮測試代碼的話,真正修改的代碼只有一行!說明之前的設(shè)計(jì)較為合理,應(yīng)對(duì)變化的能力比較強(qiáng),可維護(hù)性很好。具體修改如圖:

    修改前的代碼(傳入可重疊的MultiIntervalSet):

    修改后的代碼(傳入不可重疊的NoOverlapMultiIntervalSet):

    為了使得測試仍然保持正確,需要修改原來的測試代碼,總共修改的代碼量大約為50行,花費(fèi)時(shí)間較短。

  • Git倉庫結(jié)構(gòu)
  • 請(qǐng)?jiān)谕瓿扇繉?shí)驗(yàn)要求之后,利用Git log指令或Git圖形化客戶端或GitHub上項(xiàng)目倉庫的Insight頁面,給出你的倉庫到目前為止的Object Graph,尤其是區(qū)分清楚change分支和master分支所指向的位置。

    使用git log指令, 得到如下結(jié)果:

    可以看出,Git倉庫到目前為止的Object Graph有如下形式:

  • 實(shí)驗(yàn)進(jìn)度記錄
  • 請(qǐng)使用表格方式記錄你的進(jìn)度情況,以超過半小時(shí)的連續(xù)編程時(shí)間為一行。

    每次結(jié)束編程時(shí),請(qǐng)向該表格中增加一行。不要事后胡亂填寫。

    不要嫌煩,該表格可幫助你匯總你在每個(gè)任務(wù)上付出的時(shí)間和精力,發(fā)現(xiàn)自己不擅長的任務(wù),后續(xù)有意識(shí)的彌補(bǔ)。

    日期

    時(shí)間段

    計(jì)劃任務(wù)

    實(shí)際完成情況

    2021.6.28

    12:30-14:30

    設(shè)計(jì)IntervalSet接口并完成一個(gè)具體的實(shí)現(xiàn)類CommonIntervalSet,編寫測試

    完成

    2021.6.28

    14:30-17:00

    設(shè)計(jì)MultiIntervalSet接口并完成具體的實(shí)現(xiàn)類CommonMultiIntervalSet,編寫測試

    完成

    2021.6.28

    17:30-18:30

    設(shè)計(jì)IntervalSet和Common-MultiIntervalSet的裝飾器

    完成

    2021.6.28

    18:30-19:30

    在有無空白的維度上,設(shè)計(jì)NoBlankIntervalSet接口并完成具體實(shí)現(xiàn)類CommonNoBlankIntervalSet,編寫測試

    完成

    2021.6.28

    19:30-21:00

    在是否允許重疊的維度上,設(shè)計(jì)實(shí)現(xiàn)不允許重疊的裝飾類NoOverlapIntervalSet和NoOverlap-MultiIntervalSet,編寫測試

    完成

    2021.6.28

    21:30-22:30

    在周期性的維度上,設(shè)計(jì)實(shí)現(xiàn)周期性的裝飾類PeriodicIntervalSet和PeriodicMultiIntervalSet,編寫測試

    完成

    2021.6.29

    9:00-10:00

    實(shí)現(xiàn)Employee、Process、Course類,編寫測試

    完成

    2021.6.29

    10:00-12:30

    設(shè)計(jì)實(shí)現(xiàn)API,編寫測試

    完成

    2021.6.29

    13:30-14:00

    實(shí)現(xiàn)DutyIntervalSet、Process-IntervalSet和CourseIntervalSet,編寫測試

    完成

    2021.6.29

    14:00-17:30

    實(shí)現(xiàn)DutyRosterApp

    測試健壯性時(shí)發(fā)現(xiàn)很多不足,延期一小時(shí)完成

    2021.6.29

    19:00-22:00

    實(shí)現(xiàn)CourseScheduleApp

    測試健壯性時(shí)發(fā)現(xiàn)很多不足,延期半小時(shí)完成

    2021.6.30

    9:00-12:00

    實(shí)現(xiàn)ProcessScheduleApp

    完成

    2021.6.30

    16:30-17:30

    學(xué)習(xí)正則表達(dá)式

    完成

    2021.6.30

    18:30-20:30

    設(shè)計(jì)實(shí)現(xiàn)解析器Parser,編寫測試

    由于對(duì)正則表達(dá)式不太熟悉,延期半小時(shí)完成

    2021.6.30

    21:00-22:30

    向DutyRosterApp中加入解析文件功能

    完成

    2021.7.1

    9:00-10:00

    完善整個(gè)項(xiàng)目的注釋(spec、AF、RI、safe from exposure、test strategy)

    完成

    2021.7.1

    10:30-11:30

    修改代碼以面對(duì)新的變化

    完成

  • 實(shí)驗(yàn)過程中遇到的困難與解決途徑
  • 遇到的難點(diǎn)

    解決途徑

    對(duì)裝飾器不夠熟悉,不知如何編寫正確的裝飾器以實(shí)現(xiàn)功能。

    在網(wǎng)上查看其它應(yīng)用使用裝飾器的方法并加以總結(jié),逐漸熟悉裝飾器的使用。

    編寫APP時(shí)遇到很多的健壯性問題。

    仔細(xì)分析每個(gè)步驟中用戶可能的所有輸入,針對(duì)任何非法情況都作出提示。雖然很耗費(fèi)時(shí)間,但效果很好。

    不知如何設(shè)計(jì)正則表達(dá)式來抽取大量的文件信息。

    在學(xué)習(xí)正則表達(dá)式語法的同時(shí)進(jìn)行一些小的測試,從小的正則表達(dá)式開始,逐漸累積成復(fù)雜的正則表達(dá)式,最終實(shí)現(xiàn)對(duì)文件的解析。

  • 實(shí)驗(yàn)過程中收獲的經(jīng)驗(yàn)、教訓(xùn)、感想
  • 實(shí)驗(yàn)過程中收獲的經(jīng)驗(yàn)和教訓(xùn)
  • 設(shè)計(jì)ADT時(shí)一定要考慮得全面且清楚,在最終決定實(shí)現(xiàn)方案后再編寫代碼,否則在后面的應(yīng)用實(shí)現(xiàn)中發(fā)現(xiàn)問題時(shí)只能再重新設(shè)計(jì)。同時(shí),在編寫APP時(shí),一定要事先考慮健壯性的問題,不然,在編寫完代碼之后進(jìn)行測試時(shí)會(huì)遇到很多的健壯性問題,這時(shí)再去修改就會(huì)使得代碼很臃腫,可讀性變差,出錯(cuò)的可能性更高。

  • 針對(duì)以下方面的感受
  • 重新思考Lab2中的問題:面向ADT的編程和直接面向應(yīng)用場景編程,你體會(huì)到二者有何差異?本實(shí)驗(yàn)設(shè)計(jì)的ADT在五個(gè)不同的應(yīng)用場景下使用,你是否體會(huì)到復(fù)用的好處?
  • 面向ADT的編程需要從實(shí)際中進(jìn)行抽象,并進(jìn)行合理的設(shè)計(jì),有很好的可擴(kuò)展性和可復(fù)用性;而直接面向應(yīng)用場景編程只針對(duì)特定應(yīng)用,每次更換應(yīng)用場景時(shí)都要重新編程,擴(kuò)展性很差,只適合簡單的應(yīng)用場景。本實(shí)驗(yàn)中設(shè)計(jì)一個(gè)ADT就可以應(yīng)用到三個(gè)不同的場景,大大縮短了開發(fā)時(shí)間。

  • 重新思考Lab2中的問題:為ADT撰寫復(fù)雜的specification, invariants, RI, AF,時(shí)刻注意ADT是否有rep exposure,這些工作的意義是什么?你是否愿意在以后的編程中堅(jiān)持這么做?
  • 這些工作使得客戶端了解各方法的功能但無法得知內(nèi)部具體實(shí)現(xiàn),可以防止內(nèi)部變量被客戶端惡意修改,時(shí)刻檢查表示不變量,保證安全性。雖然這些工作有些麻煩,但卻是好的軟件必須具備的,因此我愿意在以后編程中堅(jiān)持這么做。同時(shí),在復(fù)雜的軟件開發(fā)過程中,好的注釋可以節(jié)省大量閱讀代碼的時(shí)間,使得開發(fā)時(shí)間大大降低。

  • 之前你將別人提供的API用于自己的程序開發(fā)中,本次實(shí)驗(yàn)?zāi)銍L試著開發(fā)給別人使用的API,是否能夠體會(huì)到其中的難處和樂趣?
  • 由于API面向的場景是廣泛的,因此開發(fā)難度很大,但是一旦開發(fā)一個(gè)好的API,就可以在大量場合中應(yīng)用,大大提高代碼的復(fù)用性。

  • 你之前在使用其他軟件時(shí),應(yīng)該體會(huì)過輸入各種命令向系統(tǒng)發(fā)出指令。本次實(shí)驗(yàn)?zāi)汩_發(fā)了一個(gè)解析器,使用語法和正則表達(dá)式去解析輸入文件并據(jù)此構(gòu)造對(duì)象。你對(duì)語法驅(qū)動(dòng)編程有何感受?
  • 語法驅(qū)動(dòng)編程為實(shí)際應(yīng)用提供了很大的便利,用戶無需繁瑣地一行一行地輸入信息,而只需提供一個(gè)文件以及一定的語法規(guī)則即可。而對(duì)于應(yīng)用的開發(fā)者,只需根據(jù)語法規(guī)則編寫代碼,即使改變語法規(guī)則也可以很快地修改實(shí)現(xiàn)代碼,有很好的可維護(hù)性。

  • Lab1和Lab2的大部分工作都不是從0開始,而是基于他人給出的設(shè)計(jì)方案和初始代碼。本次實(shí)驗(yàn)是你完全從0開始進(jìn)行ADT的設(shè)計(jì)并用OOP實(shí)現(xiàn),經(jīng)過五周之后,你感覺“設(shè)計(jì)ADT”的難度主要體現(xiàn)在哪些地方?你是如何克服的?
  • ADT的難度主要體現(xiàn)在抽象上。一個(gè)好的ADT既不能過于具體,也不能過于抽象。需要從大量應(yīng)用場景中尋找共性,抽象的程度也很難把握。對(duì)于這種情況,只能反復(fù)的推敲,比較不同設(shè)計(jì)方案的差異并選擇最好的ADT設(shè)計(jì)方案。

  • “抽象”是計(jì)算機(jī)科學(xué)的核心概念之一,也是ADT和OOP的精髓所在。本實(shí)驗(yàn)的五個(gè)應(yīng)用既不能完全抽象為同一個(gè)ADT,也不是完全個(gè)性化,如何利用“接口、抽象類、類”三層體系以及接口的組合、類的繼承、設(shè)計(jì)模式等技術(shù)完成最大程度的抽象和復(fù)用,你有什么經(jīng)驗(yàn)教訓(xùn)?
  • 接口、抽象類、類的抽象程度一定是逐漸降低的,將所有應(yīng)用的共性抽象為接口,然后在抽象類以及類中添加新的特性。通過接口的組合可以形成新的接口,并可以具備不同接口中的抽象。類的繼承也可以增加更具體的新的特性。同時(shí),使用正確的設(shè)計(jì)模式可以使得代碼的可復(fù)用性和可維護(hù)性最大化。

  • 關(guān)于本實(shí)驗(yàn)的工作量、難度、deadline。
  • 難度適中,可以接受,但是工作量很大,尤其是APP的編寫要花費(fèi)很長時(shí)間。雖然給了三周時(shí)間,但是和其他課的實(shí)驗(yàn)、大作業(yè)、考試有重疊,總體上時(shí)間還是很緊。希望各課程的老師可以相互協(xié)調(diào)一下實(shí)驗(yàn)安排。

  • 到目前為止你對(duì)《軟件構(gòu)造》課程的評(píng)價(jià)。
  • 逐漸理解了軟件構(gòu)造過程中獨(dú)有的思路和方法,也逐漸適應(yīng)了與之前完全不同的編程過程。通過Lab3大量的代碼訓(xùn)練,自己的編程水平也有了極大的提高。

    總結(jié)

    以上是生活随笔為你收集整理的2021哈工大软件构造Lab3的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。