web-dev-qa-db-ja.com

メソッド引数としてのブール値は受け入れられませんか?

私の同僚は、メソッド引数としてのブール値は受け入れられませんと述べています。それらは列挙型に置き換えられます。最初は何のメリットもありませんでしたが、例を挙げてくれました。

何がわかりやすいですか?

file.writeData( data, true );

または

enum WriteMode {
  Append,
  Overwrite
};

file.writeData( data, Append );

今、私はそれを手に入れました! ;-)
これは間違いなく、2番目のパラメーターとしての列挙によってコードがはるかに読みやすくなる例です。

それで、このトピックについてあなたの意見は何ですか?

122
Thomas Koschel

ブール値は「はい/いいえ」の選択肢を表します。 「はい/いいえ」を表したい場合は、ブール値を使用します。これは自明のはずです。

ただし、2つのオプションのどちらを選択しても、どちらも明らかに「はい」または「いいえ」ではない場合は、列挙型の方が読みやすい場合があります。

130
skaffman

列挙型は、3番目(またはそれ以上)の選択肢が必要な場合に、将来の変更も可能にします。

50
simon

問題を最もよくモデル化したものを使用してください。あなたが与える例では、列挙型がより良い選択です。ただし、ブール値の方が優れている場合もあります。これはあなたにとってより理にかなっています:

lock.setIsLocked(True);

または

enum LockState { Locked, Unlocked };
lock.setLockState(Locked);

この場合、ブールオプションを選択する可能性があります。これは、非常に明確で明確であり、ロックに3つ以上の状態が含まれることはないと確信しているためです。それでも、2番目の選択肢は有効ですが、不必要に複雑なIMHOです。

32
Jeremy Bourque

私にとって、ブール値も列挙型も使用することは良いアプローチではありません。 Robert C. Martinは、これを彼の クリーンコードのヒント#12:ブール引数の排除 :で非常に明確に捉えています。

ブール引数は、関数が複数のことを実行することを大声で宣言します。それらは紛らわしいので、排除する必要があります。

メソッドが複数のことを行う場合は、たとえばfile.append(data)file.overwrite(data)の2つの異なるメソッドを作成する必要があります。

列挙型を使用しても、状況は明確になりません。それは何も変更しません、それはまだ旗の議論です。

14
Pascal Thivent

アドレー・スティーブンソンが国連でゾリン大使に提起した質問を覚えていますか キューバミサイル危機

「あなたは今、世界の意見の法廷にいます、そしてあなたは答えることができますはいまたはいいえ。あなたは[ミサイル]が存在することを否定しました、そして私は私があなたを正しく理解したかどうか知りたいのですが……それがあなたの決断なら、地獄が凍りつくまで私の答えを待つ準備ができています。」

メソッドにあるフラグがバイナリ決定にピン留めできるような性質のものである場合、その決定はnever3方向またはn方向の決定に変わることはなく、ブール値を選択してください。適応症:あなたの旗はisXXXと呼ばれます。

モードスイッチである場合は、ブール値にしないでください。そもそもメソッドを書くときに思っていたよりも常にもう1つのモードがあります。

もう1つのモードのジレンマには、たとえば幽霊の出るUnix。ファイルまたはディレクトリが今日持つ可能性のある許可モードは、ファイルの種類、所有権などに応じて、モードの奇妙な二重の意味をもたらします。

13
Thorsten79

私がこれが悪いことに遭遇した2つの理由があります:

  1. 一部の人々は次のようなメソッドを書くからです:

    _ProcessBatch(true, false, false, true, false, false, true);
    _

    パラメータを混同するのは簡単すぎるため、これは明らかに悪いことであり、何を指定しているのかを見てもわかりません。ただし、1つのブール値はそれほど悪くはありません。

  2. 単純なyes/noブランチによってプログラムフローを制御するということは、2つのまったく異なる関数が1つにまとめられていることを意味する可能性があるためです。例えば:

    _public void Write(bool toOptical);
    _

    本当に、これは2つの方法でなければなりません

    _public void WriteOptical();
    public void WriteMagnetic();
    _

    これらのコードは完全に異なる可能性があるためです。あらゆる種類の異なるエラー処理と検証を行う必要がある場合もあれば、送信データを異なる形式にする必要がある場合もあります。 Write()またはWrite(Enum.Optical)を使用するだけでは、それを判断することはできません(もちろん、必要に応じて、これらのメソッドのいずれかで内部メソッドWriteOptical/Magを呼び出すこともできます)。

状況次第だと思います。 #1を除いて、私はそれについてあまり大したことはしません。

13
Sam Schutte

あなたはほとんどこれに自分で答えたと思います、最終目的はコードをより読みやすくすることだと思います、そしてこの場合列挙型はそれをしました、IMOは包括的なルールよりも最終目的を見るのが常に最善です、多分それをもっと考えてくださいガイドラインとして、つまり列挙型は一般的なbool、intなどよりもコードで読みやすいことがよくありますが、ルールには常に例外があります。

13
Tim Jarvis

列挙型の方が優れていますが、ブールパラメータを「受け入れられない」とは呼びません。小さなブール値を1つ入れて先に進むほうが簡単な場合もあります(プライベートメソッドなどを考えてください)。

7
Borek Bernard

PythonやObjective-Cのように、名前がパラメーターの機能を説明できるため、名前付きパラメーターを持つ言語ではブール値で問題ない場合があります。

file.writeData(data, overwrite=true)

または:

[file writeData:data overwrite:YES]
6
Chris Lundie

あなたの同僚がよりよく守るかもしれないいくつかの規則は次のとおりです。

  • あなたのデザインを独断的にしないでください。
  • コードのユーザーに最も適したものを選択してください。
  • 今月の形が好きだからといって、星型のペグをすべての穴に打ち込もうとしないでください。
4
Alex Worden

列挙型には明確な利点がありますが、すべてのブール値を列挙型に置き換えるだけではいけません。何が起こっているかを表すために、true/falseが実際に最良の方法である場所はたくさんあります。

ただし、メソッドの引数としてそれらを使用することは少し疑わしいです。なぜなら、それらが真/偽を見ることができるので、それらが何をすべきかを掘り下げなければ見ることができないからです実際には

プロパティ(特にC#3オブジェクト初期化子を使用)またはキーワード引数(la Rubyまたはpython)は、ブール引数を使用する場所に移動するためのはるかに優れた方法です。

C#の例:

var worker = new BackgroundWorker { WorkerReportsProgress = true };

Rubyの例

validates_presence_of :name, :allow_nil => true

Pythonの例

connect_to_database( persistent=true )

ブールメソッド引数が正しいことであると私が考えることができる唯一のことは、プロパティもキーワード引数も持たないJavaです。これが私が嫌う理由の1つですJava :-(

4
Orion Edwards

多くの場合、列挙型はブール値よりも読みやすく、拡張性が高いことは事実ですが、「ブール値は受け入れられない」という絶対的なルールは巧妙です。それは柔軟性がなく、逆効果です-それは人間の判断の余地を残しません。これらは便利なため、ほとんどの言語で基本的な組み込み型です。他の組み込み型に適用することを検討してください。たとえば、「intをパラメーターとして使用しない」と言うのはおかしなことです。

このルールはスタイルの問題であり、バグや実行時のパフォーマンスの可能性の問題ではありません。より適切なルールは、「読みやすさの理由からブール値よりも列挙型を優先する」ことです。

.Netフレームワークを見てください。ブール値は、かなりの数のメソッドのパラメーターとして使用されます。 .Net APIは完璧ではありませんが、パラメーターとしてブール値を使用することは大きな問題ではないと思います。ツールチップには常にパラメーターの名前が表示され、この種のガイダンスを作成することもできます。メソッドパラメーターに関するXMLコメントを入力すると、ツールチップに表示されます。

また、ブール値を列挙型に明確にリファクタリングする必要がある場合があることも付け加えておきます。クラスまたはメソッドパラメータに2つ以上のブール値があり、すべての状態が有効であるとは限りません(たとえば、ブール値を持つことは無効です)。両方ともtrueに設定されます)。

たとえば、クラスに次のようなプロパティがある場合

public bool IsFoo
public bool IsBar

そして、両方を同時に真にするのはエラーです。実際に得られるのは3つの有効な状態であり、次のように表現する方が適切です。

enum FooBarType { IsFoo, IsBar, IsNeither };
4
Anthony

私はそれが良いことであることに同意しませんルール。明らかに、Enumを使用すると、場合によってはより明示的または詳細なコードが作成されますが、原則として、到達するのははるかに難しいようです。

最初に例を挙げましょう。優れたコードを作成するプログラマーの責任(および能力)は、ブールパラメーターを持つことによって実際に危険にさらされることはありません。あなたの例では、プログラマーは次のように書くことで、冗長なコードと同じように書くことができます。

dim append as boolean = true
file.writeData( data, append );

または私はより一般的な方が好きです

dim shouldAppend as boolean = true
file.writeData( data, shouldAppend );

2番目:CONSTを渡しているため、指定したEnumの例は「より良い」だけです。ほとんどのアプリケーションでは、関数に渡されるほとんどの時間パラメータが変数である可能性があります。その場合、私の2番目の例(適切な名前で変数を指定する)の方がはるかに優れており、Enumではほとんどメリットがありません。

4
csmba

ブール値は、フレームワークの機能を拡張する予定がない場合にのみ受け入れられます。列挙型を拡張でき、関数呼び出しの以前の実装を壊さないため、列挙型が推奨されます。

列挙型のもう1つの利点は、読みやすいことです。

3
David Basarab

列挙型は確かにコードをより読みやすくすることができます。 (少なくとも.netでは)注意すべきことがまだいくつかあります

列挙型の基になるストレージはintであるため、デフォルト値はゼロになります。したがって、0が適切なデフォルトであることを確認する必要があります。 (たとえば、構造体は作成時にすべてのフィールドがゼロに設定されているため、0以外のデフォルトを指定する方法はありません。0の値がない場合は、intにキャストせずに列挙型をテストすることもできません。悪いスタイル。)

列挙型がコードにプライベートである場合(公開されていない場合)、ここで読むのをやめることができます。

列挙型がpublished何らかの方法で外部コードに保存されている場合、および/またはプログラムの外部に保存されている場合は、明示的に番号を付けることを検討してください。コンパイラは自動的に0から番号を付けますが、値を指定せずに列挙型を再配置すると、欠陥が発生する可能性があります。

私は合法的に書くことができます

_WriteMode illegalButWorks = (WriteMode)1000000;
file.Write( data, illegalButWorks );
_

これに対抗するには、確実ではない列挙型を消費するコード(パブリックAPIなど)は、列挙型が有効かどうかを確認する必要があります。あなたは経由でこれを行います

_if (!Enum.IsDefined(typeof(WriteMode), userValue))
    throw new ArgumentException("userValue");
_

_Enum.IsDefined_の唯一の注意点は、反射を使用し、速度が遅いことです。また、バージョン管理の問題もあります。列挙値を頻繁にチェックする必要がある場合は、次のことをお勧めします。

_public static bool CheckWriteModeEnumValue(WriteMode writeMode)
{
  switch( writeMode )
  {
    case WriteMode.Append:
    case WriteMode.OverWrite:
      break;
    default:
      Debug.Assert(false, "The WriteMode '" + writeMode + "' is not valid.");
      return false;
  }
  return true;
}
_

バージョン管理の問題は、古いコードがあなたが持っている2つの列挙型を処理する方法しか知らないかもしれないということです。 3番目の値を追加すると、Enum.IsDefinedはtrueになりますが、古いコードで必ずしも処理できるとは限りません。おっと。

_[Flags]_列挙型を使用して実行できる楽しみはさらに多く、その検証コードは少し異なります。

また、移植性のために、列挙型でcall ToString()を使用し、それらを読み戻すときにEnum.Parse()を使用する必要があることにも注意してください。ToString()Enum.Parse()は_[Flags]_列挙型も処理できるため、それらを使用しない理由はありません。念のために言っておきますが、これはさらに別の落とし穴です。コードを壊さずに列挙型の名前を変更することすらできないからです。

したがって、自分自身に問いかけるときに、上記のすべてを比較検討する必要がある場合がありますブール値だけで逃げることはできますか?

2
Robert Paulson

メソッドが次のような質問をする場合:

KeepWritingData (DataAvailable());

どこ

bool DataAvailable()
{
    return true; //data is ALWAYS available!
}

void KeepWritingData (bool keepGoing)
{
   if (keepGoing)
   {
       ...
   }
}

ブールメソッドの引数は完全に理にかなっているようです。

2
Jesse C. Slicer

方法によって異なります。メソッドが明らかに真/偽のことを行う場合、それは問題ありません。以下[これがこの方法に最適な設計であるとは言いませんが、使用法が明らかな例にすぎません]。

CommentService.SetApprovalStatus(commentId, false);

ただし、前述の例のように、ほとんどの場合、列挙型を使用することをお勧めします。 .NET Framework自体には、この規則に従わない例がたくさんありますが、それは、サイクルのかなり遅い段階でこの設計ガイドラインが導入されたためです。

2
Greg Beech

それは物事をもう少し明確にしますが、インターフェースの複雑さを大幅に拡張し始めます-追加/上書きなどの純粋なブール選択では、やり過ぎのように見えます。さらにオプションを追加する必要がある場合(この場合は考えられません)、いつでもリファクタリングを実行できます(言語によって異なります)

2
Jennifer

私見では、3つ以上のオプションが可能な状況では、列挙型が当然の選択であるように思われます。しかし、必要なのはブール値だけである状況は間違いなくあります。その場合、ブール値が機能する列挙型を使用することは、4が機能するときに7語を使用する例であると言えます。

1
Jurassic_C

私が同意するところでは、2つのオプションがあるメソッド(そして2つのオプションだけで列挙なしで読みやすくすることができるメソッド)では、列挙型が良い方法です。

例えば.

public void writeData(Stream data, boolean is_overwrite)

列挙型が大好きですが、ブール値も役立ちます。

0
Haris Krajina

オーバーロードを使用してさまざまな動作をモデル化する方が簡単な場合もあります。あなたの例から続けると、次のようになります。

file.appendData( data );  
file.overwriteData( data );

複数のパラメーターがあり、それぞれが固定されたオプションのセットを許可している場合、このアプローチは低下します。たとえば、ファイルを開くメソッドには、ファイルモード(開く/作成)、ファイルアクセス(読み取り/書き込み)、共有モード(なし/読み取り/書き込み)のいくつかの順列がある場合があります。構成の総数は、個々のオプションのデカルト積に等しくなります。当然、そのような場合、複数の過負荷は適切ではありません。

一部の言語(C#など)で正確な列挙値を検証するのは難しい場合がありますが、列挙によってコードが読みやすくなる場合があります。

多くの場合、ブールパラメータが新しいオーバーロードとしてパラメータのリストに追加されます。 .NETの1つの例は次のとおりです。

Enum.Parse(str);  
Enum.Parse(str, true); // ignore case

後者のオーバーロードは、最初のバージョンよりも新しいバージョンの.NETFrameworkで使用できるようになりました。

選択肢が2つしかないことがわかっている場合は、ブール値で十分かもしれません。古いライブラリは新しい列挙値をサポートしていない可能性があるため、バージョン管理を完全に無視することはできませんが、列挙型は古いコードを壊さない方法で拡張可能です。


[〜#〜]編集[〜#〜]

新しいバージョンのC#では、名前付き引数を使用できます。これにより、IMOは、列挙型と同じ方法でコードの呼び出しを明確にすることができます。上記と同じ例を使用します。

Enum.Parse(str, ignoreCase: true);
0
Drew Noakes

これは古い投稿の遅いエントリであり、ページのはるか下にあるため、誰もそれを読むことはありませんが、誰もそれをすでに言っていないので....

インラインコメントは、予期しないbool問題の解決に大いに役立ちます。元の例は特に凶悪です。関数declearationで変数に名前を付けようとしているところを想像してみてください。それは次のようなものになります

void writeData( DataObject data, bool use_append_mode );

しかし、例として、それが宣言だとしましょう。次に、説明のつかないブール引数について、変数名をインラインコメントに入れます。比較する

file.writeData( data, true );

file.writeData( data, true /* use_append_mode */);
0
Robert Martin

ブール値は、2つのうちの1つにしかできない明らかなトグルがある場合に意味があります(つまり、電球の状態、オンまたはオフ)。それ以外は、何を渡しているのかが明確になるように書くのが良いでしょう-例:ディスク書き込み(バッファなし、ラインバッファ、または同期)は、そのように渡す必要があります。今すぐ同期書き込みを許可したくない場合でも(したがって、2つのオプションに制限されています)、一目で何をするのかを知るために、より冗長にすることを検討する価値があります。

そうは言っても、FalseとTrue(ブール値0と1)を使用することもできます。後でさらに値が必要な場合は、関数を拡張して、ユーザー定義値(2と3など)と古い0/1値をサポートします。うまく移植されるので、コードが壊れてはいけません。

0
Dan Udey