第6章 狀態機器視圖 6.1 概述 狀態機器視圖通過對類對象的生存周期建立模型來描述對象隨時間變化的動態行為。每一個對象都被看作是通過對事件進行探測並做出回應來與外界其他部分通訊的獨立的實體。事件表示對象可以探測到的事物的一種運動變化—如接受到從一個對象到另一個對象的調用或訊號、某些值的改變或一個時間段的終結。任何影響對象的事物都可以是事件,真實世界所發生的事物的模型通過從外部世界到系統的訊號來建造的。 狀態是給定類的對象的一組屬性值,這組屬性值對所發生的事件具有相同性質的反應。換而言之,處於相同狀態的對象對同一事件具有同樣方式的反應,所以當給定狀態下的多個對象當接受到相同事件時會執行相同的動作,然而處於不同狀態下的對象會通過不同的動作對同一事件做出不同的反應。例如,當自動回覆機處於處理事務狀態或空閑狀態時會對取消鍵做出不同的反應。 狀態機器用於描述類的行為,但它們也描述用例、協作和方法的動態行為。對這些對象方面而言,一個狀態代表了執行中的一步。我們通常用類和對象來描述狀態機器,但是它也可以被其他元素所直接應用。 6.2 狀態機器 狀態機器是展示狀態與狀態轉換的圖。通常一個狀態機器依附於一個類,並且描述一個類的執行個體對接受到的事件所發生的反應。狀態機器也可以依附於操作、用例和協作並描述它們的執行過程。 狀態機器是一個類的對象所有可能的生命曆程的模型。對象被孤立地從系統中抽出和考察,任何來自外部的影響被概述為事件。當對象探測到一個事件後,它依照當前的狀態做出反應,反應包括執行一個動作和轉換到新狀態。狀態機器可以構造成繼承轉換,也能夠對並發行為建立模型。 狀態機器是一個對象的局部視圖,一個將對象與其外部世界分離開來並獨立考查其行為的圖。利用狀態機器可以精確地描述行為,但不適合綜合理解系統執行操作。如果要更好地理解整個系統範圍內的行為產生的影響,那麼互動視圖將更有用些。然而,狀態機器有助於理解如使用者介面和裝置控制器這樣的控制機。 6.3 事件 事件是發生在時間和空間上的一點的值得注意的事情。它在時間上的一點發生,沒有期間。如果某一事情的發生造成了影響,那麼在狀態機器模型中它是一個事件。當我們使用事件這個詞時,通常是指一個事件的描述符號,即對所有具有相同形式的獨立發生事件的描述,就像類這個詞表示所有具有相同結構的獨立類一樣。一個事件的具體發生叫做事件的執行個體。事件可能有參數來辨別每個執行個體,就像類用屬性來辨別每個對象。對類而言,訊號利用泛化關係來進行組織,以使不同的類共用公用的結構。事件可以分成明確或隱含的幾種:訊號事件、呼叫事件、修件事件、時間事件等。表6-1是幾種事件類型及其描述。 6-1 事件的種類
1. 訊號事件 訊號是作為兩個對象之間的通訊媒介的命名的實體,訊號的接收是訊號接受對象的一個事件。發送對象明確地建立並初始化一個訊號執行個體並把它發送到一個或一組對象。最基本的訊號是非同步單路通訊,寄件者不會等待接收者如何處理訊號而是獨立地做它自己的工作。在雙路通訊模型中,要用到多路訊號,即至少要在每個方向上有一個訊號。寄件者和接受者可以是同一個對象。 訊號可以在類圖中被聲明為類元,並用關鍵字《signal》表示,訊號的參數被聲明為屬性。同類元一樣,訊號間可以有泛化關係,訊號可以是其他訊號的子訊號,它們繼承父訊號的參數,並且可以觸發依賴於父訊號的轉換(6-1所示)。
圖6-1 訊號的等級組織 2. 呼叫事件 呼叫事件是一個對象對調用的接收,這個對象用狀態的轉換而不是用固定的處理過程實現操作。對調用者來說,普通的調用(用方法實現的調用)不會被呼叫事件所辨別。接收者不是用方法來實現操作就是觸發一個狀態轉換來實現這個操作。操作的參數即事件的參數。一旦調用的接收對象通過由事件觸發的轉換完成了對呼叫事件的處理或調用失敗而沒有進行任何狀態轉換,則控制返回到調用對象。不過,與普通的調用不同,呼叫事件的接收者會繼續它自己的執行過程,與調用者處於並行狀態。 3. 修改事件 修改事件是依靠特定屬性值的布林運算式所表示的條件的滿足。這是等到特定條件被滿足的一種聲明途徑,但是一定要小心使用它,因為它表示了一種具有時間持久性的並且可能是涉及全域的計算過程(是一種遠距離的動作,因為被測試的值可能是遠距離的)。這既有好處也有壞處,它的好處在於它將模型集中在真正的依賴關係上—一種當給定條件被滿足時發生的作用—而不是集中在測試條件的機制上。缺點在於它使修改系統潛在值和最終效果的活動之間的因果關係變得模糊了。測試修改事件的代價可能很大,因為原則上修改事件是持續不斷的。而實際上,又存在著避免不必要的計算的方法。修改事件應該僅用在當一個具有更明確表達形式的通訊形式顯得不自然時。 請注意監護條件與修改事件的區別。監護條件只是在引起轉換的觸發事件觸發時和事件接收者對事件進行處理時被賦值一次。如果它為假,那麼轉換將不會被激發,條件也不會被再賦值。而修改事件被多次賦值直到條件為真,這時轉換也會被激發。 4. 時間事件 時間事件代表時間的流逝。時間事件既可以被指定為絕對形式(天數),也可以被指定為相對形式(從某一指定事件發生開始所經曆的時間)。在高層模型中,時間事件可以被認為是來自整個世界的事件;在實現模型中,它們由一些特定對象的訊號所引起,這些對象既可能是作業系統也可能是應用中的對象。 6.4 狀態 狀態原因了一個類對象生命期中的一個時間段。它可以用三種附加方式說明:在某些方面性質相似的一組對象值;一個對象等待一些事件發生時的一段時間;對象執行持續活動時的一段時間。雖然狀態通常是匿名的並僅用處於該狀態時對象進行的活動描述,但它也可以有名字。 在狀態機器中,一組狀態由轉換相串連。雖然轉換串連著兩個狀態(或多個狀態,如果圖中含有分支和結合控制),但轉換隻由轉換出發的狀態處理。當對象處於某種狀態時,它對觸發狀態轉換的觸發事件很敏感。 狀態用具有圓形拐角的矩形表示。6-2所示。 圖6-2 狀態 6.5 轉換 從狀態出發的轉換定義了處於此狀態的對象對外界發生的事件所做出的反應。通常,定義一個轉換要有引起轉換的觸發事件、監護條件、轉換的動作和轉換的目標狀態。表6-2 列出了幾種轉換和由轉換所引起的隱含動作。
表6-2 轉換的種類及隱含動作
1. 外部轉換 外部轉換是一種改變活動狀態的轉換,它是最普通的一種轉換。它用從源狀態到目標狀態的箭頭表示,其他屬性以文字串附加在箭頭旁(6-3所示)。 圖6-3 外部轉換 2. 觸發事件 觸發事件是引起轉換的事件。事件可以有參數,以供轉換的動作使用。如果一個訊號有後代,那麼訊號中的任一個後代都可以引起轉換。例如,如果轉換將Mouse Button 作為觸發器,那麼Mouse Button Down可以觸發這個轉換(6-1)。 事件並不是持續發生的,它只在時間的一點上發生。當一個對象接收到一個事件時,如果它沒有空閑時間來處理事件,就將事件儲存起來。對象一次只處理一個事件,在對象處理事件時轉換必須激發,事件過後是不會被記住的(某些特殊的延遲事件除外,在觸發一個轉換前或處延遲被解除前,這類事件被儲存起來)。如果兩個事件同時發生,它們被每次處理一個。沒有觸發任何轉換的事件被簡單地忽略或遺棄,這並不是一個錯誤,忽略不想要的事件要比詳細指明所有事件容易得多。 3. 監護條件 轉換可能具有一個監護條件,監護條件是一個布林運算式。監護條件可以引用對象的屬性值和觸發事件的參數。當一個觸發事件被觸發時,監護條件被賦值。如果布林運算式的值為“真”,那麼觸發事件即,使轉換有效。如果布林運算式的值為“假”,則不會引起轉換。監護條件只能在觸發事件發生時被賦值一次。如果在轉換髮生後監護條件由原來的“假”變為“真”,則因為賦值太遲而不能觸發轉換。 從一個狀態引出的多個轉換可以有同樣的觸發事件,但是每個轉換必須具有不同的監護條件。當其中一個監護條件滿足時,觸發事件會引起相應的轉換。通常,監護條件的設定要考慮到各種可能的情況以確保一個觸發事件的發生應該能夠引起某些轉換。如果有些情況沒有考慮到,一個觸發事件沒有引起任何轉換,那麼在狀態機器視圖中要忽略這個事件。一個事件的發生只能同時引起一個轉換(在一個控制線程中)。如果一個事件可能引起多個轉換,那麼其中只有一個轉換有效。如果兩個相互矛盾的轉換同時有效,則無法確定到底發生了哪個轉換。這兩個轉換隨機地發生一個,或者由系統的實現細節決定究竟發生哪一個,但是對建模者來說,無法預料這種轉換產生的後果。 4. 完成轉換 沒有標明觸發事件的轉換是由狀態中的活動的完成引起的(即完成轉換)。完成轉換也可以帶一個監護條件,這個監護條件是在狀態中的活動完成時被賦值的(而不是完成以後)。 5. 動作 當轉換被引起時,它對應的動作被執行。動作是原子性的,一般是一個簡短的計算處理過程,通常是一個賦值操作或算術計算。另外還有一些動作,包括給另一個對象發送訊息、調用一個操作、設定傳回值、建立和銷毀對象,沒有被定義的控制動作用外部語言來進行詳細說明。動作也可以是一個動作序列,即一系列簡單的動作。動作或動作序列的執行不會被同時發生的其他動作影響或終止。按照UML中的概念,動作的執行時間非常短,與外界事件所經曆的時間相比是可以忽略的,因此,在動作的執行過程中不能再插入其他事件。然而,實際上任何動作的執行都要耗費一定時間,新到來的事件必須被安置在一個隊列中。 整個系統可以在同一時間執行多個動作。我們說動作是原子性的,並不是說整個系統是原子性的。系統能夠處理硬體的中斷和多個動作的時間共用。動作在它的控制線程中是原子性的。一旦開始執行,它必須執行到底並且不能與同時處於活動狀態的動作發生互動作用。但動作不能用於表達處理過程很長的事物。與系統處理外來事件所需要的反應時間相比,動作的執行過程應該很簡潔,否則系統不能夠做到即時響應。 一個動作可以使用觸發事件的參數和對象的屬性值作為運算式的一部分。
表6-3列出了各種動作及描述。
表6-3 動作的種類
6. 狀態改變 當動作執行完畢後,轉換的目標狀態被啟用,這時會觸發出口動作或入口動作的執行。 7. 嵌套狀態 狀態可以被嵌套在其他的組成狀態之內(看下一段)。從一個外部狀態出發的轉換可以應用於這個狀態所有的內部嵌套狀態。任何一個內部嵌套狀態被啟用時,轉換都有可能發生。組成狀態可用於表達例外和異常,因為組成狀態上的轉換適用於所有它所嵌套的狀態,不需要每個嵌套狀態顯式地單獨處理異常。 8. 入口和出口動作 一個跨越多個嵌套層次的轉換可能會離開或進入某個狀態。只要轉換進入或離開某個狀態,則該狀態可能包含要被執行的動作。進入一個狀態可能會執行一個依附於該狀態的入口動作。如果轉換離開初始狀態,那麼在轉換的動作和新狀態的入口動作被執行前,執行該狀態的出口動作。 入口動作通常用來進行狀態所需要的內部初始化。因為不能迴避一個入口動作,任何狀態內的動作在執行前都可以假定狀態的初始化工作已經完成,不需要考慮如何進入這個狀態。同樣,無論何時從一個狀態離開都要執行一個出口動作來進行後處理工作。當出現代表錯誤情況的高層轉換使嵌套狀態異常終止時,出口動作特別有用。出口動作可以處理這種情況以使對象的狀態保持前後一致。入口動作和出口動作原則上依附於進來的和出去的轉換,但是將它們聲明為特殊的動作可以使狀態的定義不依賴狀態的轉換,因此起到封裝的作用。 9. 內部轉換 內部轉換有一個源狀態但是沒有目標狀態。內部轉換的激發規則和改變狀態的轉換的激發規則相同。由於內部轉換沒有目標狀態,因此轉換激發的結果不改變本狀態。如果一個內部轉換帶有動作,它也要被執行,但是沒有狀態改變發生,因此也不需要執行入口和出口動作。內部轉換用於對不改變狀態的插入動作建立模型(如,記錄發生的事件數目或建立協助資訊屏)。 儘管入口動作和出口動作的執行是由進入或離開某狀態的外部轉換所引起的,除了使用保留字entry 和exit 代替觸發事件名稱之外,入口和出口動作使用與內部轉換相同的標記法。 一個自身轉移會激髮狀態上的入口動作和出口動作的執行(從概念上來講,自身轉換從一個狀態出發後又會到自身狀態),因此,自身轉換不等價於內部轉換。圖6-4 說明了入口動作、出口動作和內部轉換。
圖6-4 內部轉、入口動作和出口動作 6.6 組成狀態 一個簡單狀態沒有子結構,只帶有一群組轉換和可能的入口和出口動作。組成狀態是一個被分解成順序的或並發的子狀態的狀態。表 6-4 列出了各種狀態。 表6-4 狀態的種類
將狀態分解成互斥的子狀態是對狀態的一種專門化處理。一個外部狀態被細分成多個內部子狀態,每一個子狀態都繼承了外部狀態的轉換。在某一時間只有一個子狀態處於啟用狀態。外部狀態表達了每一個內部狀態都具有的條件。 進入或離開一個組成狀態的轉換會引起入口動作或出口動作的執行。如果有多個組成狀態,跨越多個層次的轉換會引起多重入口動作(最外層最先執行)和出口動作(最內層最先執行)的執行。如果轉換帶有動作,那麼這個動作在入口動作執行後,出口動作執行前執行。 組成狀態也可能在其內部具有一個初始狀態。組成狀態邊界上的轉換隱含為初始狀態上的轉換。一個新對象起始於它的最外層的初始狀態。如果一個對象到達了它最外層狀態的終止狀態,那麼該對象將被銷毀。初始狀態、終止狀態、入口動作和出口動作封裝了狀態的定義,使狀態的定義與進出狀態的轉換無關。 圖6-5展示了一個狀態的順序分解,其中包括一個初始狀態。這是售票系統的狀態機器模型。 圖6-5 狀態機器 將一個狀態分解成並發的多個子狀態代表相互獨立的平行處理過程。當進入一個並發超狀態時,控制線程的數目增加;當離開一個並發超狀態時,控制線程的數目減少對於每一個狀態而言,並發通常依靠不同的對象實現,但是,並發子狀態還可以代表一個單獨狀態內部的邏輯並發關係。圖6-6展示了選修一門大學課程的並發分解。
圖6-6 帶有並發組成狀態的狀態機器
圖6-7 子機器狀態 通常,可以在一個狀態機器中使用其他狀態機器的一部分,這種可重用性會帶來一些方便。狀態機器可以命名,並可以用名字引用其他一個或多個狀態機器。目標狀態機器是一個子機器,引用這個子機器的狀態叫做子機器引用狀態。它的含義是在需要引用的地方用一個狀態機器來替換原有內容。一個狀態機器可以包含一個活動,即可以包含一個處理過程或一個需要消耗時間才能完成的持續過程或是可以被中斷的事件,而子機器不能圖6-7示範了子機器的引用。 進入一個子機器引用狀態的轉換會啟用目標子機器的初始狀態。要進入其他狀態的子機器,需要在子機器引用狀態中安置一個或多個樁狀態。樁狀態用於在子機器中標識狀態。
|