官术网_书友最值得收藏!

第1章 面向對象軟件開發方法

1.1 概述

計算機發展早期軟件開發的個體化特點,造成了許多錯誤的認識和做法,表現為忽視軟件需求和軟件分析的重要性,認為軟件開發就是寫程序并使之運行。隨著軟件規模的增大,計算機軟件開發和維護過程中遇到了一系列嚴重問題,出現了軟件危機。

在軟件開發工作中,以下幾個觀念常常被忽略。

① 軟件的生命周期(參見圖1.1)大體分為規劃、分析、設計、編碼、維護等一系列階段組成。

圖1.1 軟件生命周期

在開發過程中,每個階段都可以直接和間接地回饋到前面的階段。整個軟件生命周期是一個迭代、漸增的開發過程,這種迭代過程不僅貫穿整個軟件生命周期,并且表現在每個階段中,特別是在分析(全局分析、局部分析)和設計(全局設計、局部設計) 階段。

② 程序只是完整產品的一個組成部分,Boehm對軟件的定義是:“軟件是程序、數據和開發、使用和維護程序所需要的所有文檔。”

③ 在軟件開發不同階段進行錯誤修改所付出的代價是很不相同的,在后期引入一個變動比在早期引入變動,代價高達2~3個數量級,所以做好軟件定義時期的工作是降低軟件成本,提高軟件質量的關鍵。

④ 軟件維護是極端艱巨復雜的工作,需要花費極大的代價,統計數據表明,軟件維護費用占軟件總費用的55%~70%,所以要采取措施提高軟件的可維護性,減少維護費用。

為了解決軟件危機,必須要用現代工程的概念、原理、技術和方法進行軟件開發、管理和維護。

軟件開發不是某種個體勞動的神秘技巧,而應該是一種組織良好、管理嚴密、各類人員協同配合、共同完成的工程項目。另外,在軟件開發的每個階段都有許多煩瑣重復的工作需要做,應當學會使用軟件輔助工具,在適當的軟件工具輔助下,開發人員可以把工作做得既快又好。

1.2 軟件生命周期各階段的基本任務

軟件生命周期是指一個軟件項目被提出并著手實施開始,到該軟件報廢或停止使用為止。軟件生命周期由軟件定義、軟件開發和運行維護(也稱為軟件維護) 三個時期組成,每個時期又進一步劃分成若干個階段:

● 問題定義

● 可行性研究

● 需求分析

● 總體設計

● 詳細設計

● 編碼和單元測試

● 綜合測試

● 運行與維護

(1)問題定義

確定“要解決的問題是什么?”通過調研,寫出關于問題性質、工程目標和工程規模的書面報告,并得到客戶的確認。

(2)可行性研究

確定對于問題定義階段所確定的問題是否有行得通的解決方法。研究并論證軟件系統的可行性,對方案進行選擇并形成可行性分析報告。

(3)需求分析

這個階段的任務主要是確定所完成的系統必須具備哪些功能,并用正式文檔準確地記錄結果,這個文檔通常稱為系統規格說明書。

(4)總體設計(概要設計)

軟件設計的一條基本原理就是,程序應該模塊化,也就是說,一個程序應該由若干個規模適中的模塊按合理的層次結構組織而成。因此,總體設計的主要任務就是設計程序的體系結構,也就是確定程序由哪些模塊組成及模塊間的關系。

(5)詳細設計(模塊設計)

將解法具體化,確定應該怎樣具體地實現這個系統。主要工作是模塊詳細設計。模塊詳細設計包括:模塊的詳細功能、算法、數據結構、模塊間的接口等設計,擬訂模塊測試方案。詳細設計的工作體現在詳細規格說明書。

(6)編碼和單元測試

根據模塊詳細規格說明書,把詳細設計的結果翻譯成用選定的語言書寫的程序。還要對模塊程序進行測試,驗證模塊功能及接口與詳細設計文檔的一致性,并形成單元測試報告。

(7)綜合測試

通過各種類型的測試(及相應的調試)使軟件達到預定的要求。

● 集成測試:根據設計的軟件結構,把經過單元測試檢驗的模塊按某種選定的策略裝配起來,在裝配過程中對程序進行必要的測試。

● 驗收測試:按照規格說明書的規定,由用戶對目標系統進行驗收。

● 現場測試或平行運行:平行運行就是同時運行新開發出來的系統和將被它取代的舊系統,以便比較新舊兩個系統的處理結果。

用正式的文檔資料把測試計劃、詳細測試方案以及實際測試結果保存下來,作為軟件配置的一個組成部分。

(8)運行與維護

維護階段的關鍵任務是,通過各種必要的維護活動使系統持久地滿足用戶的需要。

● 改正性維護:診斷和改正在使用過程中發現的軟件錯誤。

● 適應性維護:修改軟件以適應環境的變化。

● 完善性維護:根據用戶的要求改進或擴充軟件使它更完善。

● 預防性維護:修改軟件為將來的維護活動預先做準備。

每項維護活動都應該經過提出維護要求(或報告問題),分析維護要求,提出維護方案,審批維護方案,確定維護計劃,修改軟件設計,修改程序,測試程序,復查驗收等一系列步驟。

使用面向對象方法解決問題的過程可以大體劃分為面向對象分析(Object Oriented Analysis, OOA)、面向對象設計(Object Oriented Design, OOD)、面向對象編程(Object Oriented Programming, OOP)和面向對象測試(Object Oriented Test, OOT)等步驟。

面向對象分析的主要作用是明確使用程序的用戶、用戶可以進行的操作,以及數據的輸入、輸出和儲存,并且用標準化的面向對象模型規范地表述這些內容,最后形成面向對象分析模型,即OOA模型。在分析問題時,要抽取所有需要的對象實體,然后確定這些對象的狀態和行為,以及它們之間的相互關系。一般來說,解決一個問題會涉及多個對象,所以這些對象之間的關系一定要明確,從而反映出整個程序的功能和狀態。

面向對象設計是將在面向對象分析步驟中創建的OOA模型加以擴展并得到面向對象設計步驟中的OOD模型。面向對象設計在OOA模型的基礎上引入界面管理、任務管理和數據管理三部分的內容,進一步擴充OOA模型。界面管理負責整個系統的人機對話界面的設計,任務管理負責處理整個程序資源管理功能的工作及設置客戶與服務器之間的接口,數據管理負責設計程序與數據庫的交換方式。面向對象設計還需要明確每個類方法的參數、返回值、功能等,以及各類之間的相容性和一致性的驗證,對各個類、類內成員的訪問權限的嚴格合理性的驗證,也包括驗證對象類的功能是否符合用戶的需求。

面向對象編程就是具體的程序編寫階段,其主要過程是先選擇一種合適的面向對象編程語言,再用選定的語言編寫程序實現設計步驟中對各個對象的詳盡描述,然后將編寫好的各個類根據其關系集成為整個程序,最后通過各種實例測試找出程序的漏洞并改善程序,最終完成整個軟件的開發。

1.3 面向對象分析

1.3.1 確定客戶需要什么

人們通常認為確定客戶需要什么是一件非常容易的事,只需向客戶詢問就能輕松獲得。其實它遠比我們想象的困難,原因主要有三個。

① 首先是客戶說不清需求。有些客戶對需求只有模糊不清的感覺,自然不能很好地用語言描述出來;有些客戶心里非常清楚想要什么,但卻表達不清楚。少數客戶本身就懂得軟件開發,能把需求說得清清楚楚,這樣的需求分析將會非常輕松、愉快,但這種可能性很小。

② 其次是需求自身不斷變化。有些客戶對目標系統的預期一天一個想法,一拍腦袋就變一個主意,這是軟件開發人員最頭疼的事情,但是也只能面對現實尋找策略應對這種情況。分析人員在進行需求分析時就要注意以下兩點:

● 盡可能地分析清楚哪些是穩定的需求,哪些是易變的需求。以便在進行系統設計時,將軟件的核心建筑在穩定的需求上。

● 在合同中一定要說清楚“做什么”和“不做什么”。如果合同不清晰明確,日后會引起很多糾紛。

③ 最后就是需求分析人員和用戶交流中產生誤解。

分析人員和目標系統客戶,一方是計算機專業人員,另一方是系統應用領域專家。雖然彼此都對對方的領域有所了解,但離專業人員還是存在一定的距離,這樣他們在溝通交流中就不可避免地可能會產生誤解。

由于需求分析存在眾多困難,所以對軟件需求分析人員的要求是非常高的。他們通常都是資深的計算機專家,同時具備豐富的業務領域知識和良好的溝通技能。

1.3.2 需求階段概述

需求階段的第一步是理解應用領域,也就是目標系統應用的特定環境,例如銀行、證券公司、學校、政府等。一旦開發團隊充分了解應用領域,就可以實施目標系統的系統建模,一種很主流的建模方法就是使用統一建模語言UML來描述目標系統的業務邏輯。通過模型,開發團隊可以和目標系統的用戶進行充分交流以確定客戶的業務需求,確定客戶的最終需求是一個反復迭代的過程,經過多次溝通、理解、修正才能比較客觀地確定客戶對系統的真實需求。

1.3.3 理解應用域

為了有效地挖掘出客戶的真實需求,技術人員必須熟悉目標系統的應用領域。如果不了解系統的業務領域,很難向客戶提出有意義的問題,所以不可能成功完成系統的需求分析。理解應用域的方法就是親臨目標系統將來應用的真實環境,去了解目標系統將完成哪些業務,這些業務手工完成的流程是怎樣的,目前的業務流程有哪些優點和不足,等等,只有完全弄明白了這些問題,才有可能與客戶進行充分且有效的交流。

1.3.4 用例建模

使用UML中的“用例圖”描述擬建軟件與外部環境之間的關系。一個用例表示一個外部角色Actor(例如,用戶或其他外部軟件環境)與擬建軟件之間一個單獨交互。將所有的用例集中在一起,就可以描述一個OO軟件的總體功能需求。例如,一個網上拍賣系統的拍賣過程用例圖(見圖1.2)。

圖1.2 拍賣過程用例圖

用例圖在軟件的建模過程中是必不可少的。

1.4 面向對象設計

1.4.1 有效應用設計模式

眾所周知,人是一種經驗性的動物。我們常說:據我所知,據我所了解,據我的經驗,等等。生活中很多事情都是依靠自己或他人的經驗,軟件設計也類同。建筑大師Christopher Alexander說過:“每一個模式描述了一個在我們周圍不斷重復發生的問題,以及該問題的解決方案的核心。這樣,你就能一次又一次地使用該方案而不必做重復勞動。”他所指的是建筑領域,軟件設計模式則是描述軟件設計過程中某一類常見問題的一般性的解決方案。1995年, Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides四人合著了一本經典巨作《設計模式——可復用面向對象軟件的基礎》,該書奠定了堅實的面向對象設計模式理論的基礎,它介紹了23種基本設計模式的結構、特性和使用方法。《設計模式》一書被軟件人士當作“模式的圣經”,是所有面向對象分析設計人員必讀的書籍。四位作者被大家稱為“四人幫”(Gang of Four, GoF)。

由于《設計模式——可復用面向對象軟件的基礎》一書奠定了設計模式的地位,人們通常所說的設計模式隱含地表示“面向對象設計模式”。但這并不意味“設計模式”就等于“面向對象設計模式”,也不意味著GoF 23種模式就表示了所有的“面向對象設計模式”。除了“面向對象設計模式”外,還有其他設計模式。除了GoF 23種設計模式外,還有更多的面向對象設計模式。

面向對象設計模式精髓是隱藏在背后的三條面向對象設計原則和設計理念:適應需求的變化;針對接口編程,而不要針對實現編程;優先使用聚合,而不是繼承。

根據目標系統的特點和需要,選擇合適的模式加以應用,應用模式后最大的優點就是系統可以適應需求的變化,從而具有更好的可復用性、可擴展性和可維護性。

1.4.2 類建模

使用“類圖”描述所設計的軟件中包含的類,以及這些類之間的靜態關系,從而描繪了整個軟件的靜態組成和結構。

1.類的一般描述圖

(1)類名

類名即類的名稱。

(2)屬性

屬性描述的語法:

            visibility name [N] : type = initialValue {property-string}

① visibility:屬性的可見性:+ 表示public; # 表示protected; - 表示private。

② name:屬性的名稱。

③ [N]:屬性的多值性:[2..*]表示屬性能接受多值;如果屬性描述中無此項,則表示該屬性只允許接受一個值。

④ type:屬性的實現類型(依賴于實現語言的規范)。

⑤ initialValue:屬性的初始值。

⑥ property-string:表示屬性的一些無法用上述語法描述的特性。例如,一個只讀屬性(如C++的const成員或Java的final成員), 則property-string將可以設置為{frozen}。

(3)操作

操作描述的語法:

            visibility name (parameter-list) : return-type {operation-string}

① visibility:操作的可見性:+ 表示public; # 表示protected; - 表示private。

② name:操作的名稱。

③ parameter-list:操作的形參列表,表中允許有多個形參,形參之間由逗號分隔。每個參數的表示語法:kind name : type = defaultValue。

● kind:表示參數的作用分類

in表示參數向操作中傳入一個值。

out表示參數從操作中提取一個值。

inout表示參數既可以向操作中傳入一個值又可以從操作中提取一個值。

● name:表示形參名。

● type:表示形參類型。

● defaultValue:表示形參的默認值。

④ return-type:操作的返回類型(因編譯器而異)。

⑤ operation-string:表示操作的一些無法用上述語法描述的特性。例如,一個操作是抽象操作,則operation-string可以設置為{abstract}。

2.類的相互關系圖

用于描述類之間的靜態關系。在關系圖中的類圖除了類名部分不能省略外,其他兩部分都可以根據需要省略。主要的靜態關系有:

(1)歸納關系(Generalization)

歸納關系表示兩個類之間的繼承和派生關系,如右圖中的類Employee是類Manager的超類(基類),而類Manager是類Employee的子類(派生類)。

(2)關聯關系(Association)

關聯關系表示兩個類之間的關聯關系,如下圖中類Employee和類Corporation之間的關聯關系。

圖中employeeBy是Employee的屬性成員,其類型為Corporation。通過該屬性employeeBy使兩個類關聯;0..1表示關聯的多樣性,此關聯表示一個Employee對象不會被超過一個Corporation對象所雇用。同樣,employees是Corporation的屬性成員,其類型為Employee。*表示一個Corporation對象允許有0至任意數量的Employee對象,即雇用任意多個雇員。上述兩端關聯關系也可以用兩條箭頭線分別表示,箭頭線的方向是從關聯屬性所在類指向屬性的類型類。

下面的關聯關系表示同一類的兩個以上不同對象間的關聯。它的含義是該雇員可以管理1~10個其他員工。

注意,參與一個關聯的兩個對象常常是獨立存在的,即關聯關系中的一個對象的存在與否不會影響到所關聯的另一個對象的存在。

(3)類之間的聚合和合成關系

表示對象之間的“整體”和“部分”之間的關系,即在整體和部分之間可能存在生命期的依賴性。合成關系表示當整體不再存在時,部分同時被銷毀的緊密關系。例如,下圖中的Window和Slider、TitleBar、Panel之間的關系。

聚合關系表示當整體不再存在之后,部分還會繼續存在的關系。例如,類Orchestra和Performer之間的關系。當然這種關系也可以通過前面介紹過的關聯關系表示。但是,聚合關系可以很形象地說明一些概念。如本例中說明:樂隊是由演奏人員所組成的,但樂隊的生存期與演奏人員的壽命并不存在緊密依賴的關系。

(4)類模板和類模板的實例化

類模板是具有成員類型參數的類。例如,下圖中的類就是一個具有兩種成員類型參數T和Y的類模板,和將該類模板的成員類型實例化后的類。

(5)類的實例——對象

對象是按照類定義創建的實例,在對象的圖形描述中實例被命名,同時類的各個屬性被賦予特定的值。例如,下圖就是一個用Employee類創建的對象john_1。

顯然,在建模的主要工作——類設計中,使用類圖是十分必要的,也是十分方便的。

1.4.3 狀態圖建模

狀態圖最適合用于顯示一個類對象在經歷一系列相關用例的過程中,所呈現的不同狀態。它們可以幫助我們更好地理解一個OO程序中單一對象的生命期行為。狀態圖主要由三種圖示構件組成:

① 對象狀態:對象的一個狀態用圓角矩形表示。在該矩形中,狀態名稱用粗體字顯示在矩形最上方;如果還有額外信息,用一條線將這些額外信息與狀態名分隔開,以“do:/”為前綴的信息項目表示處于此狀態下的一個活動。開始和結束是兩個特殊狀態。

② 轉換路徑:對象從一個狀態轉換到另一個狀態用一條箭頭線(箭頭指向轉換后的狀態)表示。

③ 狀態轉換標簽:描述引起狀態轉換的原因,描述語法是

            Event[Guard]/Action

該語法的含義是:事件Event的出現將導致以Guard為條件的狀態轉換結果為TRUE。但在實際轉換到新狀態之前,動作Action必須先執行。由于對象從一個狀態到其他狀態只能進行一次轉換,因此從一個給定狀態可能出現的所有轉換路徑必須相互排斥。注意,轉換描述的三個部分(事件、條件和動作)都是可選擇的。

圖1.3描述了一次拍賣程序中Buyer對象的一個狀態圖。

圖1.3 Buyer對象的一個狀態圖

顯然,使用狀態圖對類對象的深入研究是有效的,但并不是必不可少的。在建模的過程中是否需要使用狀態圖對類對象的生存過程進行狀態進行,一般與軟件的復雜程度有關。通常情況下,一個簡單的軟件建模中,不需要繪制狀態圖。

1.4.4 順序圖建模

順序圖按時間順序描述了參與功能事務的一組對象在功能事務的執行過程中的交互操作。構成順序圖的要素如下(見圖1.4)。

圖1.4 順序圖的要素

圖1.5的順序圖描述了在網上拍賣事務中,銷售者對象和競拍者對象,以及完成一次拍賣事務所需要的其他服務對象之間相互協作的順序圖。

圖1.5 對象之間相互協作的順序圖

圖中的各種類型的交互消息必須遵循以下6種語法規則:

① 狀態消息:由一個對象將一個狀態值轉送給另一個對象,例如:[bidAcceptable]。

② 方法名:一個對象調用另一個對象的方法。例如:selectItem( )。

③ *[迭代依據]方法:其中“*”是迭代標記。這個命名方法在目標對象的多個實例上調用,至于具體在哪個實例上調用則由方括號中的表達式控制。例如:*[for all items in Auctionlist] getCurrentMaxBid( )。

④ flag := 方法:flag被設置為TRUE或FALSE,這取決于在這條消息接收對象上調用這個指定的方法所產生的結果。例如:minAcceptBidExceeded := check( )。

⑤ [條件]方法:只有在條件滿足時才在消息接收對象上調用這個指定的方法。例如:[minAcceptBidExceeded] notifySeller( )。

⑥ 特殊符號new:表示創建消息接收對象的新實例。例如:[bidAccepttable] new。

由于順序圖既能較好地描述功能事務執行過程中各個參與對象的動作順序,又能較好地描述對象之間的交互操作,因此,一般多用順序圖來描述類之間的動態交互關系。

1.4.5 協作圖建模

協作圖著眼于參與功能事務的一組對象在功能事務的執行過程中的相互協作關系,而不按照執行過程的時間順序描述對象之間的交互操作。協作圖雖然在表示對象之間的交互操作方面優于順序圖,但在其他方面都不及順序圖,因此,一般只用于順序圖所描述的類之間動態交互關系的補充。上述的網上拍賣事務的協作圖如圖1.6所示。

圖1.6 網上拍賣事務的協作圖

不難看出,使用類的交互關系圖(特別是“順序圖”)對于類設計中研究類的屬性和行為,最后確定類的結構和接口設計是必不可少的,也是十分方便的。

1.4.6 活動圖建模

活動圖描述如何通過一組相互協作的活動產生一個期望的結果。在創建活動圖時需要注意的一個重要問題是:確認那些可以同時執行的子活動(通過多進程或多線程)以及那些必須線性順序執行的子活動。活動圖中的主要圖示構件如下所示。

① 活動:用橢圓矩形表示。

② 觸發器:用指向活動和從活動出來的箭頭線表示。一個活動有流入觸發器,表示這個活動可以在接收到這個觸發器時執行,在絕大多數情況下前一個活動的成功執行就是觸發器源(流出觸發器)。對于具有多個流入觸發器的活動而言,它是非連接性的。這意味著如果這個活動收到其中一條觸發,目標活動將會執行。

③ 同步杠:用一條雙線或一條粗黑線表示。一些活動流入它,一些活動自它流出,并且它帶有附加條件[condition]。對于流入活動,同步杠是連接性(conjunctive)的,即所有流入的活動必須在控制到達同步杠之前成功執行。這些流入活動的成功執行受制于同步杠的附加條件。對于流出活動,同步杠表示這些活動允許并發執行,彼此之間相互獨立。

④ 判定活動:在絕大多數情況下,判定活動是通過測試一個布爾值來實現的。這類活動是由一個菱形表示的,如下圖所示。

圖1.7顯示了拍賣處理過程的活動圖。圖中那些可以并發進行的活動是從標簽為“fork”的同步杠中流出的。標簽為“merge”的同步杠用于表示一個條件,就是發生在該同步杠上的多個活動必須同時成功結束,控制流才能繼續向前。特別要注意從P到Q的活動段。在P處與同步杠相關聯的條件是

            *[for each item in the group]

這里的“*”表示多個觸發器,就是說它指定了從P到Q所顯示的活動線程實際上由一組平行的線程所組成,每個線程都由圖中所示的活動組成。因此,在P處源于同步杠的并發路徑的數量等于購買者感興趣的那組項目的數量。

圖1.7 拍賣處理過程的活動圖

不難看出,UML的活動圖與一般的程序流程圖在形狀和作用上是類似的,但活動圖的描述能力要遠遠超過一般的程序流程圖。在模型中使用活動圖來討論和描述軟件的部分和整個運行過程的活動也是必要的。

1.4.7 用戶界面設計

1.交互設計

用戶界面設計的目標就是使軟件讓用戶能簡單使用。任何軟件功能的實現都是通過人和計算機的交互完成的,因此,人的因素應作為設計的核心被體現出來,界面設計的原則如下:

① 執行有較大破壞性的動作前要求確認。

② 允許用戶非惡意錯誤,系統應保護自己不受致命錯誤的破壞。

③ 只顯示與當前用戶語境環境有關的信息。

④ 盡量減少用戶輸入動作的數量。

⑤ 維護信息顯示和數據輸入的一致性。

⑥ 允許兼用鍵盤和鼠標。

⑦ 使用軟件最終用戶的語言,而不是計算機專用術語。

⑧ 詳盡的幫助信息。

⑨ 允許工作中斷,例如:本次操作到一半,可以把中間狀態暫存,下次啟動軟件可以繼續完成。

⑩ 有清楚的錯誤提示,誤操作后,軟件提供有針對性地提示。

?提供快速反饋,給用戶心理上的暗示,避免用戶焦急。如時間較長的操作響應期間,可以出現進度條顯示操作進度。

2.視覺設計

① 堅持圖形用戶界面(GUI)設計原則:界面直觀、對用戶透明:用戶使用軟件后對界面上對應的功能一目了然、不需要過多培訓就可以使用軟件。

② 界面一致性原則:在界面設計中應該保持界面的一致性。一致性既包括使用標準的控件,也指使用相同的信息表現方法,如在字體、標簽風格、顏色、術語、顯示錯誤信息等方面確保一致。

1.5 面向對象編程

1.5.1從設計到C++代碼

為了把面向對象設計結果順利地轉變成面向對象程序,首先應該選擇一種適當的程序設計語言。面向對象的程序設計語言適合用來實現面向對象設計結果。事實上,具有方便的開發環境和豐富的類庫的面向對象程序設計語言,是實現面向對象設計的最佳選擇。

良好的程序設計風格對于面向對象實現來說格外重要。它既包括傳統的程序設計風格準則,也包括與面向對象方法的特點相適應的一些新準則。

在軟件生命周期中,編碼工作在整個項目中所占的比例最多不會超過1/2,通常在1/3的時間,所謂磨刀不誤砍柴工,設計過程完成的好,編碼效率就會極大提高。有人說,用C語言編寫代碼,代碼量超過10 000行就會失控,用C++,這個限制可以放寬到100 000行。這說明面向對象語言要比非面向對象語言能夠支持更大規模的軟件。

我們知道,在一個小企業中,沒有什么管理成本,管理人員也很少。但是一個大企業,一般有很多的管理人員,因為企業規模增大之后,需要付出管理成本。這些管理人員的主要職責,就是管理那些能夠直接產生效益的工作人員。我們還知道,現代社會存在的基礎是分工,分工明確,每個人做的事情少而精,這樣整個社會就會有效率。而面向對象正體現了這兩種思路。首先,一個面向對象的軟件,其中會有很多看起來什么都不做的代碼,只是簡單地把對它的調用轉給另一個對象的方法,有的只是做一些判斷,或是類型處理。這些代碼看起來有些浪費。但是它們就好像是企業的管理者一樣,雖然消耗了一些資源(所幸的是,面向對象語言設計的高效性讓這種資源消耗是可接受的),但是它們能夠讓其他產生效益的代碼工作更加有效。其次,面向對象非常注重類的職責,每個類處理的事情集中而單一,每個方法的目標都十分專一。一件事情一般是幾個類共同完成的,而不是像傳統方法那樣,一個函數一包到底。

1.5.2 編程舉例

實驗描繪:編寫一個能模擬簡單猜撲克牌大小游戲的應用程序。該游戲的名稱為Hi-Low,其玩法和規則如下所述。

1.玩法

① 洗牌:每盤游戲開始之前,使撲克牌的排列順序充分隨機。

② 發牌:每局開始時,從未使用的撲克牌集合中順序發給玩家5張撲克牌(明牌)。

③ 猜點:從未使用的撲克牌集合中按順序取出一張撲克牌(即莊家的暗牌),要求玩家將手中的第一張撲克牌和這張莊家的暗牌進行比較,確定哪張牌大?

④ 積分:玩家確定回答后,翻開被猜的撲克牌(暗牌變成明牌),同時根據玩家回答的正確與否顯示相應的提示,并為玩家的游戲成績積分。然后將這兩張已經比較過的牌回收到已經使用過的撲克牌集合中,玩家手中的下一張撲克牌成為新的一張撲克牌。

⑤ 對玩家手中剩余的撲克牌順序重復第③和④步操作,直至玩家手中不再有剩余的撲克牌時,一局游戲結束。

⑤ 如果未使用的撲克牌集合中的撲克牌數目多余10張,則從第②步開始進行本盤游戲的下一局。

⑦ 詢問玩家是否繼續進行下一盤游戲,如果繼續,則從第①步開始進行新的一盤游戲。

2.規則

① 積分規則:

● 猜中1次,積1分;猜錯1次,不積分。

● 1局中連續猜中3次,除正常積分外獎勵1分;連續猜錯3次,罰1分。

● 1局全部猜中,除正常積分外獎勵3分;1局全部猜錯,罰3分。

● 玩家的最低積分為0,即不出現負分。

② 牌面大小比較規則:每張撲克牌的牌面由花色(梅花Club、方塊Diamond、紅心Heart和黑桃Spade)和牌點(A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K)組成。

確定兩張撲克牌牌面大小的規則有兩條:

● 如果兩張牌面的牌點不同,則牌面大小僅與牌點有關,而與牌面的花色無關。牌點的大小順序為:2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < 10 < J < Q < K < A。

● 如果兩張牌面的牌點相同,則牌面大小僅與牌面的花色有關。花色的大小順序為:Club < Diamond < Heart < Spade。

3.要求

① 按照面向對象的思想分析題意要求實現的需求,合理地分解對象類。

② 設計和實現各個組成類。

③ 在主函數main( )中,創建各個組成類對象,并通過這些類對象使用類功能實現游戲Hi-Low。

④ 要求編寫編程文檔,文檔內容包括:

● 繪制各個組成類的類圖與類圖之間的靜態關系圖。

● 各個組成類的類定義描述。

● 主要功能函數的算法描述。

● main( )函數的流程圖。

4.分析

分析各種撲克牌游戲,可以歸納出如下幾條規律。

① 一副撲克牌,即撲克牌的全集是有54張具有不同牌面的撲克牌組成的。

② 任何一種撲克牌游戲都需要使用由n張撲克牌組成的集合,該集合可以是一副撲克牌的全集,也可以是一副撲克牌的子集,甚至可以是多副撲克牌的并集。例如Hi-Low游戲需要使用一副撲克牌中除大小王牌以外的52張牌組成的子集。

③ 任何一種撲克牌游戲都會根據自己的玩法和規則,確定一種若干步特定操作構成的游戲程式。

④ 每一種撲克牌游戲都會根據積分規則確定一個相對獨立的積分計算器,依據游戲結果為玩家計算積分。

根據上述分析,可以考慮進行如下的對象類分解。

① 將撲克牌對象設計為一個類Card,用于定義標示每一張特定撲克牌的牌面(花色和牌點)和顯示操作。為此該類可以用如下屬性和操作描述。

屬性

● 索引標志屬性index作為每張撲克牌唯一標志,是確定牌面花色和牌點的依據。該屬性應聲明為私有,它的值域為0~53的整數值,每一個index值與一張特定牌面的花色和牌點相對應:

0~12對應梅花Club的A 2 3 4 5 6 7 8 9 10 J Q K

13~25對應方塊Diamond的A 2 3 4 5 6 7 8 9 10 J Q K

26~38對應紅心Heart的A 2 3 4 5 6 7 8 9 10 J Q K

39~51對應黑桃Spade的A 2 3 4 5 6 7 8 9 10 J Q K

52對應小王牌L Trump

53對應大王牌B Trump

顯然,index除以13所得到的整數商可以表示花色,而index模13所得到的余數恰恰是牌點。大小王牌的index值除外。

● 牌面顯示屬性face。在圖形用戶界面的應用程序中該屬性是描述牌面圖形的復雜圖形類;而在控制臺文本界面的應用程序中該屬性可以是字符數組類型, 用于存放描述牌面的字符串。例如:“C-A”表示梅花A, “D-6”表示方塊6, “H-10”表示紅心10,“S-Q”表示黑桃Q, “LT”表示小王牌,“BT”表示大王牌等。該屬性應聲明為私有。

操作

● 構造函數:無參數,便于創建對象數組。

● 初始化操作Init:依據傳入參數值,為索引標志屬性index賦值,并確定牌面顯示屬性face。該操作應向外提供服務。

● 獲取標志操作Index:返回索引標志屬性index的當前值。該操作應向外提供服務。

● 顯示牌面操作Show:輸出牌面顯示屬性face。該操作應向外提供服務。

② 考慮到結構的合理性,可以將計算游戲積分所需要屬性和操作封裝在一個類中。該類可以命名為Counter,它所包含的屬性和操作如下。

屬性

● 積分屬性score:用于記錄游戲的積分值。為了能記錄足夠大的積分值,該屬性應聲明為長整型long。

● 連續標志屬性sequence:用于存放玩家當前猜點操作的連續狀態:

連續猜中時,sequence > 0(連續猜中的次數)。

連續猜錯時,sequence < 0(連續猜錯的次數)。

即未連續猜中,也未連續猜錯時,sequence = 0。

操作

● 構造函數:將積分屬性score和連續標志屬性sequence初始設置為0。

● 累計積分操作accumulate:根據玩家當前猜點操作的結果(布爾類型值,過實參傳遞該操作)修改玩家當前的連續猜中狀態sequence,并根據猜點操作的結果和連續猜中狀態,按積分規則玩家累計積分。該操作應向外提供服務。

● 顯示積分操作Show:顯示游戲積分值。該操作應向外提供服務。

● 清除連續標志操作ClearSequece:在游戲新的一盤開始前,設置Sequece為0。

③ 根據撲克牌游戲的玩法和規則,將撲克牌游戲設計成一個類。在類的定義中描述游戲所需要使用的撲克牌集合和所需要的輔助屬性,以及滿足游戲玩法和規則的各種操作。本題中的撲克牌游戲類可以直觀地命名為HiLow,它應該包括如下屬性和操作。

屬性

● 撲克牌集合屬性container:存放HiLow游戲所要使用的52張撲克牌(大小王牌除外)。該屬性是一個Card類型的數組。

● 已用牌索引屬性usedIndex:指示container中游戲已經使用過的撲克牌的索引。每盤游戲開始時,該屬性應被初始化為0(container的第一個元素的下標值);以后每次從container獲取一張牌(為玩家每發一張牌,取一張被猜的牌)后,屬性值加1。該屬性的值域為0 ≤ usedIndex≥ 51。

● 玩家牌索引屬性playIndex:指示當前玩家手中正在進行猜點操作的牌在container中的索引。每局游戲開始時,該屬性值 = usedIndex;以后每次猜點操作完成后,屬性值加1。該屬性的值域為0 ≤ playIndex ≥ 51。

● 積分計算器屬性counter:該屬性是一個Counter類對象,用于為游戲提供積分管理。

操作

● 構造函數:完成HiLow游戲對象的創建。在此創建操作中要完成各個屬性必要的初始化,其中對container中每個元素的初始化是委托card的Init操作完成的。

● 洗牌操作Shuffle:該操作應向外提供服務,用于container中的元素(撲克牌)進行隨機排列。模擬真實的洗牌操作可以通過52次將container中的兩個隨機下標索引的元素進行交換操作來實現。產生隨機下標可以調用庫函數rand來完成,52次交換操作就需要調用rand 104次。值得注意的是, 在第一次調用rand之前應該調用另一個庫函數srand為隨機數發生器播種,即初始隨機值,否則每次洗牌所產生的104個隨機值序列都是一樣的。獲得這個初始隨機值的最好方法就是調用庫函數time獲取當前的時間值作為初始隨機值。因此調用srand為隨機數發生器播種的表達式可以寫為

            srand((unsigned)time(NULL));

庫函數rand和srand的原型聲明在系統頭文件stdlib.h中;庫函數time的原型聲明在系統頭文件time.h中。另外,庫函數rand所產生隨機數的值域范圍是0至整型數最大值,可以通過對函數的返回值模52運算,獲得值域范圍為0~51的隨機下標值。調用洗牌操作意味著新的一盤游戲的開始,因此,在上述隨機排列操作完成后,需要將usedIndex和playIndex設置為0,還需要委托積分計算器屬性counter清除積分連續標志(sequence = 0)。

● 交換操作Swap:用于container中兩個指定元素的位置交換操作,這是洗牌需要頻繁使用的操作。該操作只為HiLow對象提供私有服務,不必向外提供服務。索引兩個進行交換操作的元素的下標是作為實參傳遞給該操作的。

● 發牌操作Deal:該操作應向外提供服務,用于每次為玩家發5張撲克牌。完成這一操作只需調整usedIndex和playIndex的值,而無須發生撲克牌的獲取操作。

● 顯示玩家手中牌的操作PlaycardShow:該操作應向外提供服務,用于顯示當前玩家手中所有可以用猜牌操作的撲克牌的牌面信息。這些牌的索引范圍是playIndex至playIndex+4。

● 比較操作Compare:該操作應向外提供服務,用于一次比較玩家明牌和莊家暗牌大小的操作,并返回比較結果(布爾)標志(玩家牌大為ture,否則為false)。被猜的兩張牌的索引可以通過playIndex和usedIndex獲得(注意訪問后,需要對這兩個屬性值增1,以便索引下一次進行比較的兩張牌)。

● 顯示猜牌結果操作ResultShow:該操作應向外提供服務,用于在比較操作Compare被調用后,顯示用于比較大小的玩家牌和莊家牌的牌面信息,并根據比較結果(作為實參傳遞給操作)顯示相應的提示信息。注意,被顯示的兩張牌的索引是playIndex-1至usedIndex-1。

● 積分操作accumulate:該操作應向外提供服務,該操作是根據玩家的本次猜牌結果(布爾)標志(猜中為true,否則為false),委托積分計算器counter,完成玩家的游戲積分計算和管理。玩家的本次猜牌結果應通過布爾類型實參傳遞給操作的。

● 判斷一局結束操作IsGameOver:該操作應向外提供服務,用于提供游戲的局結束(布爾)標志(結束為ture,未結束為false)。如何判斷一局是否結束,可以通過分析playIndex值的變化規律發現。注意,如果一局結束,還需要委托積分計算器counter清除積分連續標志(sequence = 0)。

● 判斷一盤結束操作IsSetOver:該操作應向外提供服務,用于提供游戲的盤結束(布爾)標志(結束為ture,未結束為false)。如何判斷一盤是否結束,可以通過分析usedIndex值的變化規律發現。

● 顯示游戲積分ScoreShow:該操作應向外提供服務,用于顯示玩家的當前游戲積分。此操作是委托積分計算器屬性counter提供的。

在主函數main( )中創建HiLow類游戲對象,使用該對象提供各項操作功能,實現游戲的玩法所要求的操作和控制。注意,在游戲的各步操作中應提供恰當的提示信息,以便提供友好的操作界面。

5.參考實現

(1)各個組成類的類圖

① Card類

② Counter類

③ Menu類

④ HiLow類

(2)類之間的靜態關系圖

(3)各個組成類的類定義描述

① Card類

            class Card {
            private:
                int index;                 // 索引標志屬性,作為每張撲克牌唯一標志,是確定
                                           // 牌面花色和牌點的依據。值域為0~53的整數值。
                char* face;                // 用于存放描述牌面的字符串。每張撲克牌的牌面由
                                           // 花色(梅花Club、方塊Diamond、紅心Heart和
                                           // 黑桃Spade)和牌點(A, 2, 3, 4, 5, 6, 7, 8, 9,
                                           // 10, J, Q, K)組成。
            public:
                Card( );                 // 構造函數。
                ~Card( );               // 析構函數。
                void Init(int inIndex); // 依據傳入參數值,為索引標志屬性index賦值,并確
                                           // 定牌面顯示屬性face。
                int Index( ) const;      // 返回索引標志屬性index的當前值。
                void Show( ) const;      // 輸出牌面顯示屬性face。
                };

② Counter類

            class Counter {
            private:
                long score;                // 用于記錄游戲的積分值。
                int sequence;              // 用于存放玩家當前猜點操作的連續狀態:
                                           // 連續猜中時,sequence > 0(連續猜中的次數);
                                           // 連續猜錯時,sequence < 0(連續猜錯的次數);
                                        // 即未連續猜中,也未連續猜錯時,sequence = 0。
            public:
                Counter( );              // 構造函數。將積分屬性score和連續標志屬性
                                        // sequence初始設置為0。
                ~Counter( );            // 析構函數。
                void Accumulate(bool result);
                                        // 根據玩家當前猜點操作的結果(布爾類型值,通過實參
                                        // 傳遞該操作)修改玩家當前的連續猜中狀態
                                        // sequence,并根據猜點操作的結果和連續猜中狀態,
                                        // 按積分規則玩家累計積分。
                void Show( ) const;     // 顯示游戲積分值。
                void ClearSequence( );  // 在游戲新的一盤開始前,設置Sequece為0。
            };

③ Menu類

            class Menu {
            public:
                Menu( );                     // 構造函數。
                ~Menu( );                   // 析構函數。顯示退出信息。
                void PrintMainMenu( ) const
                                               // PrintMainMenu( )函數,顯示主菜單。
                void ShowChose( ) const      // ShowChose( )函數,顯示選擇信息。
                void ShowError( ) const // ShowError( )函數,顯示錯誤信息。
                void ShowGameOver( ) const
                                               // ShowGameOver( )函數,顯示一局結束信息。
                void ShowSetOver( ) const
                                               // ShowSetOver( )函數,顯示一盤結束信息。
            };

④ HiLow類

            class HiLow {
            private:
            Card container[52];        // 存放HiLow游戲所要使用的52張撲克牌(大小王牌
                                       //除外)。
            int usedIndex;             // 指示container中游戲已經使用過的撲克牌的索引。
                                       // 每盤游戲開始時,該屬性應被初始化為0(container
                                       // 的第一個元素的下標值);以后每次從container獲
                                       // 取一張牌(為玩家發牌,取一張被猜的牌)后,屬性值
                                       // 加1。該屬性的值域為0≤usedIndex≥51。
            int playIndex;             // 指示當前玩家手中正在進行猜點操作的牌在container
                                       //中的索引。每局游戲開始時,該屬性值 =usedIndex;
                                       // 以后每次猜點操作完成后,屬性值加1。
                                       // 該屬性的值域為0≤playIndex≥51。
            Counter counter;            // 該屬性是一個Counter對象,用于為游戲提供積分管理。
            void Swap(int src, int des);
                                        // 用于container中兩個指定元素的位置交換操作,這
                                        // 是洗牌需要頻繁使用的操作。
            public:
            HiLow( );                // 完成HiLow游戲對象的創建。在此創建操作中要完成
                                       // 各個屬性必要的初始化,其中對container中每個元
                                       // 素的初始化是委托card的Init操作完成的。
            ~HiLow( );              // 析構函數。
                void Shuffle( );         // 用于container中的元素(撲克牌)進行隨機排列。
                void Deal( );            // 用于每次為玩家發5張撲克牌。
                void PlaycardShow( ) const;
                                           // 用于顯示當前玩家手中所有可以用猜牌操作的撲克牌
                                           // 的牌面信息。
            bool Compare( );             // 用于一次比較玩家明牌和莊家暗牌大小的操作,并返回
                                           // 比較結果(布爾)標志(玩家牌大為ture,否則為false)。
                void ResultShow
                  (bool result) const;
                                           // 用于在比較操作 Compare 被調用后,顯示用于比較
                                           // 大小的玩家牌和莊家牌的牌面信息,并根據比較結果
                                           // (作為實參傳遞給操作)顯示相應的提示信息。
                void Accumulate
                  (bool result);
                                           // 用于在比較操作 Compare 被調用后,顯示用于比較
                                           // 大小的玩家牌和莊家牌的牌面信息,并根據比較結果
                                           // (作為實參傳遞給操作)顯示相應的提示信息。
                bool IsGameOver( );      // 用于提供游戲的局結束(布爾)標志(結束為ture,
                                           // 未結束為false)。
                bool IsSetOver( );       // 用于提供游戲的盤結束(布爾)標志(結束為 ture,
                                           // 未結束為false)。
                void ScoreShow( ) const; // 用于顯示玩家的當前游戲積分。此操作是委托積分計算
                                           // 器屬性counter提供的。
            };

(4)主要功能函數的算法描述

① Card類Init( )函數

            void Init(int inIndex)
            {
            index ← inIndex
            switch(inIndex / 13) {
                case 1: face[0] ← ' C' , break;
                case 2: face[0] ← ' D' , break;
                case 3: face[0] ← ' H' , break;
                case 4: face[0] ← ' S' , break;
            }
            face[1]←' -'
            switch(inIndex % 13) {
                    case 1: face[2] ← ' A' , break
                    case 2: face[2] ← '2' , break
                    case 3: face[2] ← '3' , break;
                    case 4: face[2] ← '4' , break;
                    case 5: face[2] ← '5' , break;
                    case 6: face[2] ← '6' , break;
                    case 7: face[2] ← '7' , break;
                    case 8: face[2] ← '8' , break;
                    case 9: face[2] ← '9' , break;
                    case 10: face[2] ← '10' , break;
                    case 11: face[2] ← ' J' , break;
                    case 12: face[2] ← ' Q' , break;
                    case 13: face[2] ← ' K' , break;
            }
            }

② Counter類Accumulate( )函數

            void Accumulate(bool result)
            {
                if (result == true) {
                    if (sequence < 0)
                        sequence = 0;
                    ++sequence;
                    if (sequence < 3)
                        ++score;
                    else if (sequence >= 3 && sequence < 5)
                        score += 2;
                    else if (sequence == 5)
                        score += 4;
                }
                else {
                    if (sequence > 0)
                        sequence = 0;
                    --sequence;
                    if (sequence > -3)
                        score = score > 0 ? --score : 0;
                    else if (sequence <= -3 && sequence > -5)
                        score = score > 2 ? score -= 2 : 0;
                    else if (sequence == -5)
                        score = score > 4 ? score -= 4 : 0;
                }
            }

③ HiLow類Shuffle( )函數

            void Shuffle( )
            {
            srand((unsigned)time(NULL));
                indexSrc = rand( ) % 52;
                indexDes = rand( ) % 52;
                Swap(indexSrc, indexDes);
                usedIndex = 0;
                playIndex = 0;
                counter.ClearSequence( );
            }

(5)main( )函數的流程圖

① main( )函數的流程圖

② 循環中的流程圖

③ 執行游戲的流程圖

1.6 面向對象測試

在軟件開發活動中,為保證軟件的可靠性,人們研究并使用多種方法進行分析、設計及編碼實現。由于軟件本身是無形態、復雜的、知識高度密集的產品,不可避免地產生錯誤,因此軟件開發總伴隨著軟件質量保證的活動,而軟件測試是主要活動之一。軟件測試代表了需求分析、設計和編碼的最終復審。

1.6.1 白盒測試技術

把程序看成裝在一個透明的白盒子里,也就是測試人員完全了解程序的結構和處理過程,對程序執行的邏輯路徑進行測試。通過在不同的關鍵點檢查程序的狀態,確定實際狀態是否和預期狀態一致。因此,白盒測試又稱為結構測試、邏輯測試。

白盒測試作為結構測試方法,是按照程序內部的結構測試程序,檢查程序中的每條通路是否能夠按照預定要求工作,因此其最主要的技術是邏輯覆蓋技術。邏輯覆蓋包括:語句覆蓋、判定覆蓋、條件覆蓋、判定/條件覆蓋、條件組合覆蓋和路徑覆蓋等。

① 語句覆蓋就是設計若干個測試用例,運行所測程序,使得每一個可執行的語句至少執行一次。

② 判定覆蓋又稱分支覆蓋就是設計若干個測試用例,不僅使每個語句至少執行一次,而且程序中的每個取真分支和取假都至少執行一次。

③ 條件覆蓋就是設計若干個測試用例,使判定表達式的每個條件的所有可能的取值都至少執行一次。

④ 判定/條件覆蓋要求選取足夠多的測試數據使每個判定表達式都取得各種可能的結果,從而測試比較復雜的路徑。

⑤ 條件組合覆蓋構造一組測試實例,保證使判斷語句中的各邏輯條件取值的可能組合至少執行一次。

⑥ 路徑覆蓋是設計足夠的測試用例,使得程序中所有可能路徑都至少被執行一次。

1.6.2 黑盒測試技術

把程序看成一個黑盒子,完全不考慮程序的內部結構和處理過程。黑盒測試是在程序接口進行的測試,它只檢查程序功能是否能按照規格說明書的規定正常使用,程序是否能適當地接收輸入數據產生正確的輸出信息,并且保持外部信息的完整性。黑盒測試又叫功能測試或輸入/輸出驅動測試。

通過黑盒測試主要發現以下錯誤:

● 是否有不正確或遺漏的功能。

● 在接口上,能否正確地接受輸入數據,能否產生正確的輸出信息。

● 訪問外部信息是否正確。

● 性能上是否能滿足要求。

常見的黑盒測試方法有等價類劃分、邊界值分析、錯誤猜測、基于故障的測試、因果圖法等。

(1)等價類劃分法

等價類劃分是一種典型的黑盒測試方法,使用這一方法時,完全不考慮程序的內部結構,只依據程序的規格說明來設計測試用例。使用這一方法設計測試用例要經歷劃分等價類(列出等價類表)和選取測試用例兩步。

所謂等價分類,就是把輸入數據的可能值劃分為若干等價類(等價類是指某個輸入域的子集合)。在該集合中,各個輸入數據對于揭露程序中的錯誤都是等價的)。因此,可以把全部輸入數據合理地劃分為若干等價類,在每一個等價類中取一個數據作為測試的輸入條件,這樣就可以少量的代表性測試數據,來取得較好的測試結果。

(2)邊界值分析法

長期經驗表明:大量的錯誤是發生在輸入或輸出范圍的邊界上,而不是在輸入范圍的內部。邊界是指相當于輸入等價類和輸出等價類而言,稍高于其邊界值及稍低于其邊界值的一些特定情況。使用邊界值分析方法設計測試用例,應對確定的邊界,選取正好等于,剛剛大于,或剛剛小于邊界的值作為測試數據,而不是選取等價類中的典型值或任意值作為測試數據。

(3)錯誤猜測

猜測被測程序中哪些地方容易出錯,并據此設計測試實例。錯誤猜測法依賴于測試人員的直覺和經驗。錯誤猜測的基本思想是某處發現了缺陷,則可能會隱藏更多的缺陷,在實際操作中,列出程序中所有可能的錯誤和容易發生的特殊情況,然后依據經驗做出選擇。

(4)基于故障的測試

基于故障的測試是證明某個規定的故障不存在于代碼中。

(5)因果圖法

如果在測試時必須考慮輸入條件的各種組合,可使用一種適合于描述對于多種條件的組合,相應產生多個動作的形式來設計測試用例,這就需要利用因果圖。因果圖方法最終生成的就是判定表。它適合于檢查程序輸入條件的各種組合情況。

1.6.3 測試用例的編寫

測試用例要根據測試大綱來寫,而測試大綱要根據測試計劃來編寫。測試計劃起到框架作用,測試的計劃應該作為測試的起始步驟和重要環節。一個測試計劃應包括:產品基本情況調研、測試需求說明、測試策略和記錄、測試資源配置、計劃表、問題跟蹤報告、測試計劃的評審、結果,等等。

測試大綱更多的是把握住測試項的方向,測試大綱應包括測試目的、測試環境(軟件環境,硬件環境)、測試方法及測試項目。下面是一個軟件測試大綱的模板。

X X系統軟件V1.0

軟件測試大綱

1.測試目的:通過測試驗證該系統已經達到設計指標。

2.測試環境(對于C/S、 B/S結構的軟件請分別說明客戶端和服務器端的軟、硬件環境)

● 硬件環境

● 軟件環境

3.測試方法:使用以用戶文檔為基礎構造的測試用例來測試程序和數據。

4.測試項目:

a)系統安裝與卸載(對于說明書中注明由開發方提供系統安裝和配置服務的軟件,該部分可免測,請在測試方法中注明)

b)軟件功能測試(根據軟件說明書中提及的功能模塊填寫本部分,下表中內容僅為示范,行數可自由增刪,如需提供測試用例,請附在文檔后或另附文件)

c)安全可靠性(對于無安全保密性要求的軟件,本項可免測,請在欄目中注明)

d)用戶界面

e)用戶文檔

測試用例是指導怎么去執行測試。測試用例的編寫要注意以下幾個問題:

● 測試用例要根據測試大綱來編寫。

● 測試用例也要分測試項進行歸類,如業務流程測試、安裝測試、功能測試、用戶友好性測試、兼容性測試、性能測試、安全性測試,等等。

● 編寫測試用例要考慮各種情況,精力主要集中在軟件的主要業務流程和風險高的地方。最好能分出測試優先級別。

下面是一個軟件測試用例的模板。

主站蜘蛛池模板: 门源| 山阴县| 洪湖市| 永嘉县| 石棉县| 望谟县| 科尔| 甘泉县| 吉安市| 白玉县| 富蕴县| 乐清市| 吉安县| 双鸭山市| 江安县| 延安市| 桂东县| 武夷山市| 安吉县| 河东区| 黄大仙区| 会宁县| 洪泽县| 武义县| 塘沽区| 高邑县| 阳朔县| 景德镇市| 固镇县| 靖边县| 贵港市| 廊坊市| 襄樊市| 平利县| 修武县| 呼和浩特市| 天祝| 金沙县| 依兰县| 长顺县| 陈巴尔虎旗|