《数据结构与抽象:Java语言描述(原书第4版)》一1.2 说明一个包
本節(jié)書摘來華章計算機《數(shù)據(jù)結構與抽象:Java語言描述(原書第4版)》一書中的第1章 ,第1.2節(jié),[美]弗蘭克M.卡拉諾(Frank M. Carrano) 蒂莫西M.亨利(Timothy M. Henry) 著 羅得島大學 新英格蘭理工學院 辛運幃 饒一梅 譯 更多章節(jié)內容可以訪問云棲社區(qū)“華章計算機”公眾號查看。
1.2 說明一個包
在用Java實現(xiàn)包之前,需要描述它的數(shù)據(jù),并詳細說明對應于包行為的方法。我們將命名方法,選擇它們的參數(shù),確定它們的返回值類型,并寫出注釋來充分描述它們對包數(shù)據(jù)的影響。當然,我們最終的目的是寫出每個方法的Java頭和注釋,但首先我們用偽代碼來描述方法,然后用統(tǒng)一建模語言(UML)進行表示。
CRC卡的第一個行為引出一個方法,該方法返回包中當前的項數(shù)。對應的方法沒有參數(shù),它返回一個整數(shù)。使用偽代碼,我們有下列的描述:
可以使用UML將方法表示為:
且將這行添加到類圖中。
可以使用一個布爾值方法來測試包是否為空,同樣該方法沒有參數(shù)。用偽代碼及UML描述這個方法的規(guī)格說明
及
將這行添加到類圖中。
注:因為通過查看getCurrentSize是否返回0就能檢測包何時為空,所以并不真的需要操作isEmpty。但是它是所謂的便利方法(convenience method),所以很多集合都提供這樣一個操作。
現(xiàn)在想向包中添加給定的對象。可以將這個方法命名為add,并有一個表示新項的參數(shù)。可以寫出下列偽代碼:
我們可能試圖讓add作為void方法,但是會有一些情況,比如如果包滿則不能將新項添加到包中。這些情況下,我們該如何辦呢?
設計決策:當不能添加新項時,方法add將如何處理?
當add不能完成任務時,我們可采取下面兩種選擇:
什么也不做。不能添加其他的項,所以忽略這個項并且不改變包。
不改變包,但告訴客戶添加是不可能的。
第一個選擇簡單,但會讓客戶疑惑到底發(fā)生了什么。當然,我們可以規(guī)定add的前置條件,即包必須不滿。這樣客戶要負責避免將新項添加到滿包中。
第二個選擇更好一些,且它也不難說明或實現(xiàn)。我們如何告訴客戶添加是否成功?標準Java接口Collection規(guī)定,如果添加沒有成功則發(fā)生異常。稍后我們再完成這個方法,并且使用另一種方式。顯示一條錯誤信息并不是好的選擇,因為你應該讓客戶決定所有的書面輸出。因為添加操作或者成功或者不成功,所以我們可以讓方法add返回一個布爾值。
因此,可以用UML規(guī)范add方法:
其中T表
示newEntry的數(shù)據(jù)類型。
自測題1 假定aBag表示一個有有限容量的空包。寫偽代碼,將用戶提供的字符串添加到包中,直到操作失敗。
有3個動作涉及從包中刪除項:刪除所有的項;刪除任意一項;刪除某個項。假定我們用偽代碼為這些方法命名并說明其參數(shù),如下所示:
這些方法的返回類型是什么?
方法clear可以是一個void方法:我們只想要一個空包,不獲取它的任何內容。所以,在UML中方法寫為:
如果第一個remove方法從包中刪除一項,則該方法可以簡單地返回被刪除的對象。它的返回類型為T,這是包中項的數(shù)據(jù)類型。在UML中,我們有
現(xiàn)在,我們可以處理從返回null的空包中刪除對象了。
如果包中不含有某項,則第二個remove方法不能從包中刪除該項。可以讓方法返回一個布爾值,類似于add那樣,用它來表示成功與否。或者,方法可以返回被刪對象,或者,如果不能刪除這個對象則返回null。下面是用UML表示的規(guī)格說明的兩種可能版本——我們必須二選一:
或者
如果anEntry等于包中的某項,則這個方法的第一個版本將刪除該項并返回真(true)。即使方法沒有返回被刪除的項,客戶也能有方法的參數(shù)anEntry,它等于被刪除的項。故我們選擇這個版本,它與接口Collection是一致的。
自測題2 在一個類內同時具有上面描述的remove(anEntry)的兩個版本合法嗎?
解釋。
自測題3 在一個類內同時具有remove的兩個版本,一個不帶參數(shù)而另一個帶一個參數(shù),這樣合法嗎?解釋。
自測題4 給出自測題1中創(chuàng)建的滿包aBag,寫偽代碼語句,刪除并顯示包中的所有字符串。
其他的動作并不改變包的內容。其中一個動作是計數(shù)包中給定對象的出現(xiàn)次數(shù)。我們先用偽代碼后用UML說明它,如下所示。
另一個方法測試包是否含有給定對象。使用偽代碼和UML給出的規(guī)格說明如下所示。
自測題5 給定自測題1中創(chuàng)建的滿包aBag,寫偽代碼語句,找出aBag中字符串"Hello"出現(xiàn)的次數(shù),如果有的話。
最后,我們想看看包的內容。不是提供顯示包中項的方法,而是定義一個方法來返回保存這些項的數(shù)組。這樣,客戶可以按照自己的意愿顯示部分或全部的項。下面是最后這個方法的規(guī)格說明:
當方法返回一個數(shù)組時,它通常應該定義一個新的數(shù)組來返回。我們還將說明這個方法的細節(jié)。
當我們?yōu)榘械姆椒ㄌ峁┣懊婺切┮?guī)格說明時,使用UML符號來表示它們。圖1-2顯示了這些結果。
注意,CRC卡和UML并不反映所有的細節(jié),例如我們在前面的討論中提到過的假定和特殊情形。但是,在確定了這樣的條件后,你應該在每個方法的下面說明該方法應有的動作。
應該寫下你的決策,想讓方法如何動作,就像我們寫在下表中的那樣。然后,可以將這些非形式化的描述放在說明方法的Java注釋中。
設計決策:當特殊條件出現(xiàn)時會怎樣?
作為類的設計者,必須要做出決定如何處理特殊條件,并將這些決策包含在規(guī)格說明中。ADT包的文檔應該反映這些決策和前面討論的細節(jié)。
一般地,可以用幾種方式聲明特殊情形。你的方法可能
- 假定無效的情形不能發(fā)生。這個假定并不像聽起來那么幼稚。方法可以聲明一種假設(即前置條件),這是客戶必須遵守的限制。然后由客戶檢查在方法調用前這個前置條件是否滿足。例如,方法remove的前置條件可能是包為非空的。注意,客戶可以使用ADT包的其他方法,例如isEmpty和getCurrentSize,來輔助完成這個任務。只要客戶遵守這個限制,無效的情形就不會發(fā)生。
- 忽略無效情形。當給出無效數(shù)據(jù)時方法可能簡單到什么也不做。但是什么都不做會讓客戶不知道發(fā)生了什么。
- 猜測客戶的意圖。與前一個選擇一樣,這個選擇可能為客戶帶來麻煩。
返回一個表示問題的值。例如,如果客戶試圖從空包中remove一項時,remove方法應該返回null。返回的值必須是不在包中的值。 - 返回一個布爾值,表示操作的成功或失敗。
- 拋出一個異常。
注:拋出異常經(jīng)常是Java方法運行期間處理遇到的特殊事件的理想方法。方法可以簡單地報告問題而不決定要做什么。異常能讓每個客戶根據(jù)自己的特殊情形按需處理。Java插曲2將介紹異常的基本機制。
注:ADT規(guī)格說明的草稿經(jīng)常忽視或忽略你確實需要考慮的情形。你可能為了簡化草稿而有意忽略這些。一旦寫好了規(guī)格說明中的大部分內容,就可以關注這些細節(jié),而讓規(guī)格說明更完善。一個接口
隨著規(guī)格說明越來越詳細,也越發(fā)地影響到你對程序設計語言的選擇。最終,你可能為包的方法寫下Java的方法頭并將它們組織為一個Java接口,用它們來實現(xiàn)ADT的類。程序清單1-1中的Java接口含有ADT包的方法及描述它們行為的詳細注釋。回想一下,類接口不含有數(shù)據(jù)域、構造方法、私有方法或保護方法。
現(xiàn)在,包中的項將是同一個類的對象。例如,我們可以有字符串的包。為了容納類類型的項,包的方法中使用泛型數(shù)據(jù)類型(generic data type)T>來表示每個項。必須在接口名的后面寫,來說明標識符T的含義。一旦客戶選擇了具體的數(shù)據(jù)類型,編譯程序將在T出現(xiàn)的所有地方使用那個數(shù)據(jù)類型。接在本章后面的Java插曲1中,將討論如何使用泛型為ADT中的數(shù)據(jù)提供類型的靈活性。
當檢查接口時,注意前一段中提到的處理特殊情形時所做的決策。具體來說,對于add、remove及contains方法,它們每一個都返回一個值。因為我們的程序設計語言是Java,所以要注意,有一個remove方法返回一個指向項的引用,而不是項本身。
雖然不一定要在實現(xiàn)類之前寫接口,但這樣做能讓你以簡潔的方式記錄你的規(guī)格說明。然后可以將接口中的代碼用在具體類的框架中。有了接口還能為包提供數(shù)據(jù)類型,它不依賴于具體的類定義。接下來的兩章將開發(fā)包類的兩種不同的實現(xiàn)。針對接口所寫的代碼,能讓我們更易于將包的一種實現(xiàn)替換為另一種。
程序清單1-1 包類的Java接口
說明一個ADT并為它的操作寫了Java接口后,應該寫幾個使用ADT的Java語句。雖然還不能執(zhí)行這些語句(畢竟我們沒寫實現(xiàn)BagInterface的類),但我們可以用它們來確認或者修改方法的設計決策及相關文檔。這樣,可以檢查規(guī)格說明的適應性及對它的理解。最好現(xiàn)在來修改ADT的設計或文檔,而不是等到寫完實現(xiàn)后再進行。認真做這件事的額外好處是,后面可以使用這些相同的Java語句來測試你的實現(xiàn)。
自測題6 給定自測題1創(chuàng)建的包aBag,寫Java語句,顯示aBag中所有的字符串。不要改變aBag的內容。
程序設計技巧:在實現(xiàn)一個類之前寫測試程序寫Java語句來測試一個類的方法,將有助于你完全理解方法的規(guī)格說明。很明顯,在能正確實現(xiàn)方法之前必須理解它。如果你也是類的設計者,那么使用這個類可能有助于你對設計或對文檔進行理想的修改。如果在實現(xiàn)類之前做這些修改,將會節(jié)省時間。因為早晚都要寫一個程序來測試你的實現(xiàn),所以為什么不現(xiàn)在寫而獲益,而非要放到以后再寫呢?
注:雖然我們說過,包中的項屬于同一個類,但這些項也可能屬于因繼承關系而相關的類。例如,假定Bag是實現(xiàn)接口BagInterface的類。如果我們寫下面的語句創(chuàng)建類C對象的包:則aBag中可以包含類C的對象及C的任何子類的對象。
下一節(jié)看看使用包的兩個例子。后面,可以用這些例子來測試你的實現(xiàn)。
總結
以上是生活随笔為你收集整理的《数据结构与抽象:Java语言描述(原书第4版)》一1.2 说明一个包的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Visual Studio 2008 测
- 下一篇: 学习java开发培训