フラグ引数

2011年6月23日

フラグ引数とは、その値に応じて異なる操作を実行するように関数に指示する一種の関数引数です。コンサートの予約をしたいとしましょう。これには、通常とプレミアムの2つの方法があります。ここでフラグ引数を使用すると、次のようなメソッド宣言になります。

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

フラグ引数に対する私の一般的な反応は、それらを避けることです。フラグ引数を使用するのではなく、別々のメソッドを定義することを好みます。

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

私の理由は、別々のメソッドの方が、呼び出し時の意図をより明確に伝えるためです。book(martin, false)を見たときにフラグ変数の意味を覚える必要がない代わりに、regularBook(martin)を簡単に読むことができます。

複雑な実装

フラグ引数に対する私の一般的な嫌悪感には、いくつかの微妙な点と結果があります。その最初の点は、複雑な実装をどのように処理するかです。

最も単純なケースでは、フラグへの反応は、事実上異なるメソッドを呼び出すことです。

    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();
  

この状況では、通常とプレミアムの予約メソッドを、2つの間で大幅な重複なしに別々のメソッドに抽出しようとすると、混乱する可能性があります。この場合、1つのオプションは、フラグ引数を持つメソッドを保持しますが、非表示のままにすることです。

    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を呼び出すべきということです。私は、見栄えの悪い名前を使用することでこれを伝えたいと思っています。これは、必要に応じて、正規表現プローブを追加して、他の誰もそれを呼び出していないことを簡単に確認できるという利点もあります。

フラグの導出

プレミアム予約プロセスを使用するかどうかという決定が、顧客のステータスに依存する場合はどうでしょうか。エリート顧客はプレミアム予約を受け、通常の顧客は通常の対応を受けると仮定しましょう。この場合、もちろん、ブールフラグを持つべきではありませんが、顧客オブジェクト自体がフラグとして機能しているのでしょうか?

私はこれを、呼び出し元の意図を捉えることについて考えています。予約方法が顧客のステータスにのみ依存する場合、呼び出し元はプレミアム予約と通常の予約の違いを気にする必要はありません。したがって、予約ルーチンが顧客のステータスに基づいて真のメソッドを導き出すことは完全に理にかなっています。呼び出し元がどちらのメソッドを希望するかを指定する必要がある場合にのみ、異なるメソッドが必要になります。

ブール設定メソッド

これと関連しているのは、ブール設定メソッドの命名方法の問題です。ここでは、Kentのアドバイスに同意します。私は、

    void setOn();
    void setOff();
  

を見るよりも、

    void setSwitch(boolean on);
  

を見る方が良いでしょう。しかし、Kentにも同意するように、これはメソッドの使用方法に依存します。UIコントロールやデータソースなど、ブールソースからデータを取得する場合、setSwitch(aValue)よりも

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

の方が良いでしょう。これは、APIは呼び出し元にとって使いやすくするために記述するべきであるという例です。そのため、呼び出し元がどこから来ているのか分かっている場合は、その情報を考慮してAPIを設計する必要があります。これは、呼び出し元が両方の場合に、両方のスタイルを提供する場合もあることを示しています。

同じロジックがbookにも適用されます。画面にチェックボックスがあり、その値をbookに渡しているだけの場合、フラグ引数は妥当です。この例では、簡単な選択とは言えません。ほとんどの場合、bookのフラグ引数は、単純なブールセッターよりも理解するのがはるかに難しいと主張し、したがって明示的なメソッドの価値があります。