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


123

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

何が理解しやすいですか?

file.writeData( data, true );

または

enum WriteMode {
  Append,
  Overwrite
};

file.writeData( data, Append );

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

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


7
これは非常に興味深い読み物でした。このメソッドをより頻繁に実装します。
サラチップス

hrm、iveはこれを以前に行いましたが、これがどれほど優れたデザインパターンであるかを理解していません。列挙型はファイルに入りますか?
Shawn

列挙型は確かにセマンティックハメ撮りからより意味があります。別のノートでは、ファジーロジックを処理するために何人かのプログラマーが考え出したものを見るのは興味深いでしょう。
James P.

2
これが受け入れられない場合は、アドベンチャータイムのレモン男に聞いてください
ajax333221

回答:


131

ブール値は「はい/いいえ」の選択肢を表します。「はい/いいえ」を表す場合は、ブール値を使用します。

しかし、それが2つのオプションの間の選択であり、どちらも明確に「はい」でも「いいえ」でもない場合、列挙型は読みやすくなることがあります。


3
さらに、メソッド名は、引数yesまたはnoが何をするかを明確にする必要があります。
サイモン

10
この場合、状況に応じてturnLightOn()とturnLightOff()を使用したほうがよいでしょう。
スカフマン2008

14
「turnLightOn(false)」は「ライトをオンにしない」を意味しますか?混乱。
Jay Bazuzi、2008

17
いかがですかsetLightOn(bool)
Finbarr

10
後半のコメントですが、@ Jay Bazuzi:メソッドがturnLightOnと呼ばれ、falseを渡した場合、メソッドをまったく呼び出さないこともできます。falseを渡すと、ライトはオンになりません。ライトがすでにオンになっている場合、それはオフにすることを意味するのではなく、オンにしないことを意味します... 'On'および 'Off'を持つenumメソッド 'turnLight' enumがある場合、turnLight(オン)、turnLight(オフ)。私はskaffman thoに同意します。2つの異なる明示的なメソッド、turnLightOn()とturnLightOff()を使用します。(ところで:この内容はボクルズ叔父さんの本「クリーンコード」で説明されています)
Phill


32

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

lock.setIsLocked(True);

または

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

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


2
あなたの例では、2つの方法があると思います。lock.lock()lock.release()とlock.IsSetですが、すべては、使用するコードにとって最も意味のあるものに依存します。
ロバートポールソン

3
それは公平なコメントですが、特定の問題をモデル化するには多くの方法があるという大きな点も示していると思います。状況に応じて最適なモデルを使用する必要があります。また、モデルに適合するようにプログラミング環境が提供する最適なツールも使用する必要があります。
Jeremy Bourque、

私は完全に同意します:)、私は特定の疑似コードの提供についてさらにコメントしていました。私はあなたの答えに同意します。
ロバートポールソン、

14

私にとって、ブール値も列挙値も使用しないのは良い方法です。ロバートC.マーティンは、これを彼のクリーンコードヒント#12:ブール引数の排除で非常に明確に捉えています。

ブール引数は、関数が複数のことを行うことを大声で宣言します。それらは混乱を招くため、削除する必要があります。

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

列挙型を使用しても、物事が明確になるわけではありません。それは何も変更しません、それはまだフラグ引数です。


7
それは長さNのASCII文字列を受け入れる関数が128 ^ Nのことをするということではないのですか?
10

@deltyこれは深刻なコメントですか?はいの場合、文字列のすべての可能な値に頻繁にifをコーディングしますか?ブール引数の場合と可能な比較はありますか?
Pascal Thivent、2010

オブジェクト内にブール値を設定する場合は、それらは許容できると思います。完璧な例はでしょうsetVisible(boolean visible) { mVisible = visible; }。あなたが提案する代替案は何でしょうか?
ブラッド

2
@Brad show(){mVisible = true} hide(){mVisible = false}
Oswaldo Acauan 2013

@Oswaldo、それでも正しいですが、ブール値を異なる値に割り当てるために2つの異なるメソッドを使用することは、まったく意味がないと思います。setIntToOne()、setIntToTwo()、setIntToThree()がありませんか?可能な値が2つしかない場合は少しあいまいになりますが、その場合は清潔さのためにブール値を使用します。
ブラッド

13

あなたはほとんど自分でこれに答えたと思います。最終的な目的はコードを読みやすくすることです。この場合、列挙型はそれを行いました。IMOは、包括的なルールではなく、最終目的を常に確認するのが最善です。ガイドラインとして、つまり列挙型は多くの場合、汎用のブール値、整数などよりもコードで読みやすくなっていますが、ルールには常に例外があります。


13

Adlai Stevensonがキューバミサイル危機の間に国連でZorin大使に提起した質問を覚えていますか?

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

あなたの方法を持っている旗は、あなたがそれを突き止めることができるような性質のものである場合には二分決定し、その決定がされます決して 3ウェイまたはNウェイの決定に変わりない、ブールのために行きます。徴候:あなたの旗はisXXXと呼ばれます

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

one-more-modeのジレンマは、たとえば、ファイルやディレクトリがファイルタイプや所有権などに応じて、モードの奇妙な二重の意味を持つ可能性のある可能性のある許可モードであるUnixに悩まされています。


13

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

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

    ProcessBatch(true, false, false, true, false, false, true);
    

    これは明らかに悪いことです。パラメーターを混同するのは簡単であり、何を指定しているのかを見てもわからないからです。たった一つのブール値は悪くないけど。

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

    public void Write(bool toOptical);
    

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

    public void WriteOptical();
    public void WriteMagnetic();
    

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

たぶん依存していると思います。#1を除いて、私はそれについてあまり大きなことはしません。


とても良い点!1つのメソッド内の2つのブール型パラメーターは、確かにひどいように見えます(もちろん、名前付きパラメーターがあると幸運な場合を除きます)。
Yarik、

ただし、この回答は、一部の再フォーマットから利益を得る可能性があります。;-)
Yarik、

7

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


メソッドを非常に説明的にするだけで、trueまたはyesの意味が明確になります。
サイモン

6

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

file.writeData(data, overwrite=true)

または:

[file writeData:data overwrite:YES]

1
IMHO、writeData()は、名前付きパラメーターがサポートされているかどうかに関係なく、ブールパラメーターを使用する悪い例です。パラメータにどのように名前を付けても、False値の意味は明白ではありません。
Yarik、

4

いいルールだとは思わない。明らかに、Enumはいくつかのインスタンスでより明示的または冗長なコードを作成しますが、原則として到達するのははるかに難しいようです。

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

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

または私はより一般的な方を好みます

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

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


1
booleanパラメータは多くの場合許容できることに同意しますが、このwriteData()の例の場合、shouldAppendのようなbooleanパラメータは非常に不適切です。理由は簡単です。Falseの意味がすぐにわからないからです。
Yarik、

4

列挙型には明確な利点がありますが、すべてのブール値を列挙型に置き換えるだけではいけません。実際に起こっていることを表すには、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 )

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


4

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

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

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

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

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

public bool IsFoo
public bool IsBar

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

enum FooBarType { IsFoo, IsBar, IsNeither };

4

同僚が守るべきルールは次のとおりです。

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

3

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

Enumの他の利点は、読みやすいことです。


2

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

KeepWritingData (DataAvailable());

どこ

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

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

booleanメソッドの引数は、完全に理解できるようです。


いつか「空き領域があれば書き込みを続ける」を追加する必要があり、それからとにかくブールから列挙型に移動します。
Ilya Ryzhenkov 2008

そして、あなたには重大な変更があるか、時代遅れのオーバーロードがあるか、KeppWritingDataEx :)のようなものかもしれません
Ilya Ryzhenkov '25

1
@イリヤか多分あなたはしません!現在存在しない状況を作成しても、解決策は無効になりません。
Jesse C. Slicer

1
ジェシーの権利。そのような変更の計画はばかげています。意味のあることをしてください。この場合、ブール値は直感的で明確です。 c2.com/xp/DoTheSimplestThingThatCouldPossiblyWork.html
デレクパーク

@Derek、この場合、DataAvailableは常にtrueを返すため、ブール値は必要ありません:)
Ilya Ryzhenkov '28

2

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

CommentService.SetApprovalStatus(commentId, false);

しかし、あなたが言及する例のように、ほとんどの場合、列挙を使用する方が良いです。.NET Framework自体には、この規則に従わない多くの例がありますが、それは、この設計ガイドラインがサイクルのかなり後期に導入されたためです。


2

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


1
3番目の可能なオプションとしてプリペンディングはどうですか?;
Yarik、

2

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

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

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

列挙型が公開されている場合何らかの方法で外部コードにれているか、プログラム外で保存されている場合は、明示的に番号を付けることを検討してください。コンパイラーは自動的にそれらに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]列挙型を使用すると、さらに楽しいことができます。そのための検証コードは少し異なります。

私はまた、移植のために、あなたが呼び出しを使用する必要があることに注意しましょうToString()列挙型で、かつ使用をEnum.Parse()後ろにそれらを読むとき。両方ToString()Enum.Parse()扱うことができます[Flags] enumため、使用しない理由はありません。ちなみに、コードを壊すことなく列挙型の名前を変更することはできないため、これはもう1つの落とし穴です。

それで、あなたが自分自身に尋ねるとき、あなたは時々あなたは上記のすべてを比較検討する必要があります私はブールだけで逃れることができますか?


1

私見は、列挙型が3つ以上のオプションが可能なすべての状況で明白な選択になるように思われます。しかし、ブール値だけで十分な場合もあります。その場合、ブール値が機能する列挙型を使用すると、4の場合に7ワードを使用する例になります。


0

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

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


0

オーバーロードを使用して異なる動作をモデル化する方が単純な場合もあります。あなたの例から続けるには:

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

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

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

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

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

後者のオーバーロードは、.NETフレームワークの最初のバージョンよりも後のバージョンで使用できるようになりました。

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


編集

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

Enum.Parse(str, ignoreCase: true);

0

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

例えば

public void writeData(Stream data, boolean is_overwrite)

Enumが大好きですが、ブール値も役に立ちます。


0

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

インラインコメントは、予期しないbool問題を解決するのに役立ちます。元の例は特に厄介です:関数の明確化で変数に名前を付けようとすることを想像してください!それは次のようなものになるでしょう

void writeData( DataObject data, bool use_append_mode );

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

file.writeData( data, true );

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

-1

それは実際には議論の正確な性質に依存します。yes / noでもtrue / falseでもない場合、列挙型を使用すると読みやすくなります。ただし、列挙型では、基になる型の未定義の値を渡すことができるため、引数を確認するか、デフォルトの動作を許容する必要があります。


-1

例でブール値の代わりに列挙型を使用すると、メソッド呼び出しが読みやすくなります。ただし、これはC#で私のお気に入りのウィッシュアイテム、メソッド呼び出しの名前付き引数の代わりになります。この構文:

var v = CallMethod(pData = data, pFileMode = WriteMode, pIsDirty = true);

IDEでの外観に関係なく、メソッドの各パラメーターに最も適切なタイプを選択することで、プログラマーがすべきことを実行できます。

C#3.0では、コンストラクターで名前付き引数を使用できます。彼らがメソッドでこれを行うことができない理由がわかりません。


面白いアイデア。しかし、パラメータを並べ替えることができるでしょうか?パラメータを省略しますか?これがオプションの場合、コンパイラーはどのオーバーロードにバインドしていたかをどのようにして知るのでしょうか?また、リスト内のすべてのパラメーターに名前を付ける必要がありますか?
Drew Noakes、

-1

ブール値true/ falseのみ。したがって、それが何を表しているのか明確ではありません。Enum例えば、意味のある名前を持つことができOVERWRITEAPPEND列挙型が優れているので、など。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.