物件行為的動態描述:狀態圖

什麼是物件的狀態?

大部分的物件都有內部狀態, 物件對於訊息的反應都是根據其狀態來決定的, 例如:

注意:

  1. 只有功能很單純的物件是沒有狀態的, 這種物件不管在任何時候接到訊息的反應都是一模一樣的, 有內部狀態的物件其接收訊息的時序是很重要的

  2. 由物件的外部是沒辦法很容易地看出物件的狀態來的, 除非透過物件定義的特殊界面, 或是長時間地觀察物件的表現。

物件界面的使用方法是其內部狀態的外在表現:

公用成員函式與其預設的呼叫順序都是物件界面 (interface) 的一部份, 其中 "函式呼叫的順序" 就代表了物件內部狀態的存在。 使用一個物件時必須透過這一組界面、 遵循其使用規則才可以正確地得到此物件所提供的功能。 實作一個物件的時候, 必須保證 "不管其它物件如何使用此組界面, 物件都需要正確、 合理地運作, 不能夠隨便就當掉而有不合理的反應"

例如:

直覺地實作物件的狀態:

在需要記錄狀態的地方以布林變數來記錄

void open()
{
    if (!m_fOpen)
    {
        m_fOpen = true;
        do_open();
    }
}
void close()
{
    if ((m_fOpen)&&(!m_fConnected))
    {
        m_fOpen = false;
        do_close();
    }
}
void connect()
{
    if ((m_fOpen)&&(!m_fConnected))
    {
        m_fConnected = true;
        do_connect();
    }
}
void disconnect()
{
    if (m_fConnected)
    {
        m_fConnected = false;
        do_disconnect();
    }
}
void read()
{
    if (m_fConnected)
        do_read();
}
void write()
{
    if (m_fConnected)
        do_write();
}

上面程式的考慮周全了嗎? 兩個 flag 共有 4 種狀況, 可是每一個訊息處理函式中似乎都只考慮到部份的狀況, 其它的狀況呢?

以狀態圖來全盤描述物件的動態反應:

狀態圖最大的好處在於 "完整地描述物件所有可能發生的狀況", 注意下圖中必須畫出所有的狀態, 同時每一個狀態下 outgoing branch 需要包含所有可能出現的事件 (訊息)

首先 m_fOpen 及 m_fConnected 兩個 flag 可以定義出 4 種狀況, 其中有意義的有三種如下表, 表中並指定每一種狀況一個狀態的名稱:

第四種狀態 m_fOpen==false, m_fConnected==true 是不可能發生的狀態, 不予考慮。

在下面的狀態圖中我們考慮三種狀態以及六種輸入訊息 (事件), 這三種狀態是 Closed, Opened, 及 Connected, 六種訊息是 open, connect, read, write, disconnect, close

實作狀態圖表示的物件:

用單一的狀態變數來實作狀態圖中物件的所有狀態

製作訊息 open 處理函式 open() 的方法如下:

void open()
{
    if (m_state == Closed)
    {
        do_open();
        m_state = Opened;
    }
}
void close()
{
    if (m_state == Opened)
    {
        do_close();
        m_state = Closed;
    }
}
void connect()
{
    if (m_state == Opened)
    {
        do_connect();
        m_state = Connected;
    }
}
void disconnect()
{
    if (m_state == Connected)
    {
        do_disconnect();
        m_state = Opened;
    }
}
void read()
{
    if (m_state == Connected)
        do_read();
}
void write()
{
    if (m_state == Connected)
        do_write();
}

注意:

結論:

在製作每一個類別的時候, 如果能夠清楚的分析條列此類別物件的

在任何一種狀態下應該要考慮所有可能收到的訊息, 才不會在某些沒有考慮到的操作狀況下產生不合適的反應。

類別關係圖及物件關係圖是設計程式者與使用此應用程式的人心中共同的靜態應用模型 (conceptual model), 必需反映實際世界中真實的系統運作模型, 每一個不同的物件的狀態圖則是該物件的完整動態模型, 這兩種圖可以說完整地刻劃了整個物件系統, 一般同一個程式計劃中不同的設計人員在討論時並不直接交換程式碼, 而是共同討論這些圖片, 這些圖片也可以和提出系統需求的使用者來溝通, 同時也是系統標準文件中最重要的一部份。

基本上每一個類別是一個模組, 和外界其它的物件之間只透過定義好的界面來溝通, 在製作這個類別中的事件處理函式時不應該假設其它物件會以某種特定的方法來使用此物件, 這也就是為什麼在每一個狀態下我們都需要考慮所有可能發生的訊息的原因, 在很多情況下, 物件不會去處理一些不該出現的訊息, 也就是狀態圖中標示 NOP 的意義, 但是這些不該出現的訊息, 常常也設計成導致一些警告的訊息來提醒其他的物件他們的使用方式有誤, 例如上例中:

這樣子設計出來的物件的獨立性很高, 可以提供給任意其它的物件來組合系統, 各種可能出現的狀況都已經考慮到了, 不會有什麼意外的狀況發生。

其它參考資料:

C++ 程式設計課程 首頁

製作日期: 5/11/2000 by 丁培毅 (Pei-yih Ting)
E-mail: pyting@cs.ntou.edu.tw