跳轉到內容

面向物件程式設計/狀態?

來自華夏公益教科書,開放的書籍,開放的世界

狀態機

[編輯 | 編輯原始碼]

如果您不熟悉 狀態機,我們不會深入探討它們,但我們強烈建議您進一步研究它們。這對您思考過程的清晰度有好處。嚴格地說,狀態機是相當複雜的數學結構,並且可以使用數學符號推匯出許多有趣的結果。但這不是我們的興趣。我們的興趣有兩方面

  • 一方面,狀態機提供了一種清晰的圖形方式來說明可能很複雜的應用程式邏輯。首先,您確定可能的狀態。例如,伺服器可能處於 IDLE、CONNECTED、BUSY、ERROR 等狀態。然後,您定義所有可能的輸入,即客戶端可能傳送的訊息,併為每個狀態圖繪製伺服器對每個輸入的響應。當您考慮在各種狀態下對意外訊息的反應時,這有助於提高周密性。
  • 其次,狀態機是一種使用相對標準符號的標準工具。有一些工具和庫可以根據某些規範狀態機格式作為輸入自動生成程式碼。這實際上是正則表示式處理器所做的。

在面向物件程式設計中,“物件”有時是 狀態機 的實現。狀態機是行為實體的更好概念基礎——伺服器、正則表示式處理器、圖形引擎和虛擬機器都是行為實體的良好示例。

簡單物件和行為實體之間的基本區別在於,簡單物件不會根據其值改變其行為。事實上,我們傾向於將其稱為而不是狀態。簡單物件往往被儲存在集合中並被搜尋,傳遞到函式並從函式返回,並且通常像值一樣起作用。字串和矩形是經典的示例。稍後,我們將討論訊息傳遞作為方法呼叫的抽象,在簡單物件的上下文中,這可能看起來有點奇怪。

如果您仍然對物件的概念感到困惑,請下載並使用 BlueJ。大多數人在使用該 IDE 幾個小時後就會“理解”。

"狀態" 是邪惡的!

[編輯 | 編輯原始碼]

讓我們看一下 Ruby 中的示例

我們正在隨著時間推移顯示某些內容,因此我們同時處理畫素和秒。根據縮放級別,它們之間存在相關性:每秒畫素數或 PPS。

class Timeline
 {
  PPS;
  current_position_in_seconds;
  current_position_in_pixels;
 public:

  GetPosInSeconds() {current_position_in_seconds;}
  SetPosInSeconds(s) {current_position_in_seconds = s;} # oops, we're out of sync
  GetPosInPixels() {current_position_in_pixels;}
  SetPosInPixels(p) {current_position_in_pixels = p;} # oops, we're out of sync
}

在這個示例中,我們維護了無端的狀態——也就是說,我們同時以秒和畫素的形式儲存了位置。這很方便,對於此類的使用者來說也很重要,但我們在封裝方面搞砸了。每當您設定一個單位的值時,您就破壞了另一個單位的正確性。好吧,您說,這裡有一個簡單的解決方法

class Timeline
{
  PPS;
  current_position_in_seconds;
  current_position_in_pixels;
public:

  GetPosInSeconds() {current_position_in_seconds;}
  SetPosInSeconds(s)
       {
          current_position_in_seconds = s;
          current_position_in_pixels = s*PPS;
        }

  GetPosInPixels() {current_position_in_pixels;}
  SetPosInPixels(p)
       {
          current_position_in_pixels = p;
          current_position_in_seconds = p/PPS;
       }
}

這修復了先前示例中明顯的錯誤,但您為自己創造了很多工作。現在您必須在 Timeline 類中的每個方法中保持所有計算的重複,以保持一致性。可能還有數十個地方需要這樣做(相信我)。那麼當您新增其他單位(比如英寸)時會發生什麼?您必須再次重複所有這些。可以想見,您知道事情會如何發展:計算屬性、邏輯屬性、虛擬屬性,無論您想叫它們什麼。讓我們再次看一下這個示例

class Timeline
{
  PPS; # Pixels Per Second
  IPS; # Inches Per Second
  current_position_in_seconds;
public:

  GetPosInSeconds() {current_position_in_seconds;}
  SetPosInSeconds(s) {current_position_in_seconds = s;}
 
  GetPosInPixels() {current_position_in_seconds*PPS;}
  SetPosInPixels(p) {current_position_in_seconds = p/PPS; }
 
  GetPosInInches() {current_position_in_seconds*IPS;}
  SetPosInInches(i) {current_position_in_seconds = i/IPS; }
}

現在,我們假設 PPS 和 IPS 的轉換系數設定正確,為了簡單起見,但除此之外,我們極大地簡化了我們的問題。在我們接下來寫的二十個其他函式中,我們只需要關心秒,沒有一致性問題需要擔心。此外,如果我們需要將我們的基本單位從秒更改為畫素,Timeline 使用者永遠不需要知道。

華夏公益教科書