跳轉到內容

面向物件程式設計/"狀態"是邪惡的!

來自華夏公益教科書

"狀態"是邪惡的!

[編輯 | 編輯原始碼]

我們沒有足夠強調的一點是:狀態(與變化相反)是邪惡的!或者,(也許說更準確)維護不必要的狀態是所有(呃……許多)錯誤的根源。讓我們看一個例子,現在在 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
}

在這個例子中,我們正在維護不必要的 state - 也就是說,我們正在用秒和畫素來儲存位置。這很方便,對於這個類的使用者來說也很重要,但我們在這裡搞砸了封裝。無論何時你設定一個單位的值,你都會破壞另一個單位的正確性。好吧,你說,這裡有一個簡單的解決方法

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 使用者永遠不會知道。

華夏公益教科書