Flag 參數

2011 年 6 月 23 日

Flag 參數是一種函式參數,會告知函式根據其值執行不同的操作。想像一下我們想要預訂一場音樂會。有兩種預訂方式:一般和高級。若要在此處使用 Flag 參數,我們會得到類似以下內容的方法宣告

    //pseudo-code
    class Concert...
      public Booking book (Customer aCustomer, boolean isPremium) {...}
  

我對 Flag 參數的普遍反應是避免使用它們。我比較喜歡定義不同的方法,而不是使用 Flag 參數。

    class Concert...
      public Booking regularBook(Customer aCustomer) {...}
      public Booking premiumBook(Customer aCustomer) {...}
  

我這麼做的理由是,當我呼叫時,不同的方法會更清楚地傳達我的意圖。當我看到 book(martin, false) 時,我不必記住 Flag 變數的意義,我可以輕鬆地讀取 regularBook(martin)

糾結的實作

我普遍不喜歡 Flag 參數確實有一些細微差別和後果,第一個後果是該如何處理糾結的實作。

在最簡單的情況下,對 Flag 的反應實際上是呼叫不同的方法。

    public Booking book (Customer aCustomer, boolean isPremium) {
      if(isPremium) 
       // logic for premium book
      else
       // logic for regular booking
    }
  

但有時候邏輯會更糾結

    public Booking book (Customer aCustomer, boolean isPremium) {
      lorem().ipsum();
      dolor();
      if(isPremium)
        sitAmet();
      consectetur();
      if(isPremium)
        adipiscing().elit();
      else {
        aenean();
        vitaeTortor().mauris();
      }
      eu.adipiscing();
  

在這種情況下,嘗試將一般和高級預訂方法萃取到不同的方法中可能會很混亂,而且這兩個方法之間會出現大量的重複。在這種情況下,一個選項是保留具有 Flag 參數的方法,但將其隱藏起來。

    class Order...
      public Booking regularBook(Customer aCustomer) {
        return hiddenBookImpl(aCustomer, false);
      }
      public Booking premiumBook(Customer aCustomer) {
        return hiddenBookImpl(aCustomer, true);
      }
      private Booking hiddenBookImpl(Customer aCustomer,  boolean isPremium) {...}
  

重點在於,只有一般和高級預訂方法應該呼叫 hiddenBookImpl。我喜歡使用醜陋的名稱來傳達這一點,而且這樣做的好處是,如果你必須這麼做,你可以輕鬆地新增一個正規表示式探測,以確保沒有其他人呼叫它。

衍生 Flag

如果是否使用高級預訂流程的決定取決於客戶的狀態,該怎麼辦?我們假設菁英客戶獲得高級預訂,而一般客戶獲得一般待遇。在這種情況下,我們當然不應該使用布林 Flag,但客戶物件本身是否充當 Flag?

我會將此視為捕捉呼叫者的意圖。如果預訂方式僅取決於客戶狀態,則呼叫者無需在意高級預訂和一般預訂之間的差異 - 因此,預訂常式根據客戶狀態推導其真實方法是完全合理的。只有在呼叫者需要指定她想要哪一個方法時,您才需要不同的方法。

布林設定方法

與此相關的是布林設定方法的命名方式。在此我同意 Kent 的建議 - 我寧願看到

    void setOn();
    void setOff();
  

而不是看到

    void setSwitch(boolean on);
  

但再次同意 Kent,這取決於方法的使用方式。如果您從布林來源(例如 UI 控制項或資料來源)擷取資料,我寧可使用 setSwitch(aValue) 而不是

    if (aValue)
      setOn();
    else
      setOff();
  

這是一個 API 應撰寫為讓呼叫者更輕鬆的範例,因此如果我們知道呼叫者的來源,我們應在考量此資訊的情況下設計 API。這也表示如果我們以兩種方式取得呼叫者,我們有時可能會提供兩種樣式。

相同的邏輯適用於 book。如果螢幕上有核取方塊,而我們只是將其值傳遞給 book,則標記引數有一些道理。在此範例中,我不會說這是一個簡單的選擇 - 我會爭論,大多時候 book 的標記引數比簡單的布林設定器難懂多了,因此值得使用明確的方法。