「false」であるブール関数の引数に付けるコメントを修正しますか?


19

いくつかのオープンソースプロジェクトから、次のコーディングスタイルを集めました

void someFunction(bool forget);

void ourFunction() {
  someFunction(false /* forget */);
}    

falseここで何を意味するのか、私はいつも疑っています。それは「忘れる」ことを意味しますか、それとも「忘れる」は対応するパラメーターを参照しますか(上記の場合のように)、「false」はそれを無効にすることを意味しますか?

どのスタイルが最も頻繁に使用され、曖昧さを避けるための最良の方法(またはいくつかのより良い方法)は何ですか?


38
boolsの代わりに列挙型を使用します(オプションが2つしかない場合でも)
Esailija

21
一部の言語は名前付き引数をサポートしています。そのような言語では、次を使用できますsomeFunction(forget: true);
ブライアン

3
フラグ引数に関するMartin Fowlerの引数を提供する義務があると感じています(ブールセッターについても説明しています)。一般に、それらを避けるようにしてください。
FGreg

3
当たり前のことをやるには、コメントがあります。したがって、一部のコメントは変更さtruefalseてコメントが更新されないため、コードは常に自己文書化する方が適切です。あなたはAPIを変更できない場合は、これをコメントする最良の方法はあるsomeFunction( false /* true=forget, false=remember */)
マーク・Lakata

1
@Bakuriu-実際、私はおそらくまだパブリックAPIに2つの別個のメソッド(sortAscendingおよびsortDescending、または類似の)があります。さて、の内部では、両方が同じプライベートメソッドを呼び出す場合がありますが、このメソッドにはこの種のパラメーターが含まれている場合があります。実際には、言語が...私はソート方向を含まラムダ関数となり渡すと思いますおそらく何、それをサポートしている場合
時計仕掛け-ミューズ

回答:


33

投稿したサンプルコードでforgetは、フラグ引数のように見えます。(関数は純粋に仮説的なものであるため、確信が持てません。)

フラグ引数はコードの匂いです。関数は複数のことを行い、良い関数は1つのことだけを行う必要があることを示しています。

フラグ引数を回避するには、関数名の違いを説明する2つの関数に関数を分割します。

フラグ引数

serveIceCream(bool lowFat)

フラグ引数なし

serveTraditionalIceCream()
serveLowFatIceCream()

編集:理想的には、flagパラメーターを使用して関数を保持する必要はまったくありません。Fowlerがもつれた実装と呼ぶものに沿って、関数を完全に分離すると重複コードが作成される場合があります。ただし、パラメーター化された関数の循環的複雑度が高いほど、それを取り除くための引数は強くなります。


これは単なるごまかしにすぎませんが、名前が付けられたパラメーターはforget、feature envyのように聞こえます。呼び出し元が別のオブジェクトに何かを忘れるように言うのはなぜですか?より大きな設計上の問題があるかもしれません。


4
+17、ヘルメットを着用。遺体何をすべきかserveTraditionalIceCreamとのserveLowFatIceCreamように見えますか?14種類のアイスクリームの列挙型があります。
JohnMark13

13
パブリックメソッドの場合、この規則は適切ですが、JohnMarkがほのめかしているように、SRPの残りの半分は「適切なメソッドが、それが行うことを行う唯一のメソッドでなければなりません」です。この方法にはN + 1種類があります。パラメータなしのN public。これらはすべて、公開を回避しようとしているパラメータを使用して1つのプライベートメソッドを呼び出します。ある時点で、あきらめて、いまいましいパラメーターを公開するだけです。コードのにおいは、必ずしもコードをリファクタリングする必要があるとは限りません。これらは、コードレビューで再検討するに値するものにすぎません。
キース

@Aaron「機能のねたみ」とはどういう意味ですか?
オタク

27

素人の言葉で:

  • false リテラルです。
  • あなたはリテラルを渡している false
  • あなたはsomeFunction忘れないように言っています
  • あなたが言っているsomeFunctionパラメータであることを忘れていることfalse
  • あなたはsomeFunction覚えているように言っています

私の意見では、関数が次のようになった方が良いでしょう。

void someFunction(bool remember);

あなたはそれを呼び出すことができます

void ourFunction() {
  someFunction(true);
} 

または、古い名前を保持しますが、ラッパー関数を

void ourFunctionWithRemember() {
  someFunction(false);
} 

編集:

@Voracが述べたように、常にポジティブな言葉を使うよう努めてください。二重否定は紛らわしい。


15
ポジティブな言葉を使うよう常に努力するというアイデアのために+1。二重否定は紛らわしい。
ヴォラック

1
同意、素晴らしいアイデア。システムに何もしないように指示するのは混乱を招きます。ブールパラメータを受け入れる場合は、積極的に表現します。
ブランドン

ほとんどの場合、remember関数名によって意味がremember 非常に明確にならない限り、よりも具体的にしたいと思うと思います。 rememberToCleanUp* or *persistか何か。
itsbruce

14

パラメータには適切な名前を付けることができます。関数の名前を知らずに伝えるのは難しいです。私はコメントは、関数のオリジナルの著者によって書かれたと仮定し、それが合格何のリマインダーだったfalsesomeFunction手段が、誰もがその後に沿って来て、それは一見ちょっと不明です。

使用して正の変数名をCode Completeで推奨)ことは、このスニペットを読みやすくする最も単純な変更かもしれません。例えば

void someFunction(boolean remember);

その後ourFunction

void ourFunction() {
    someFunction(true /* remember */);
}

ただし、列挙型を使用すると、関数呼び出しがさらに理解しやすくなりますが、一部のサポートコードが犠牲になります。

public enum RememberFoo {
    REMEMBER,
    FORGET
}

...

void someFunction(RememberFoo remember);

...

void ourFunction() {
    someFunction(RememberFoo.REMEMBER);
}

someFunction何らかの理由で署名を変更できない場合、一時変数を使用するとコードも読みやすくなります。人間がコードを解析しやすくする以外の理由で変数を導入することで条件を単純化するようなものです。 。

void someFunction(boolean remember);

...

void ourFunction() {
    boolean remember = false;
    someFunction(remember);
}

1
remembertrueに設定することは、(あなたの例ではsomeFunction(true /* forget */);)忘れることを意味しますか?
ブライアン

2
これenumは断然最高のソリューションです。型が(boolつまり同型である)として表現できるからといって、そのように表現する必要があるという意味ではありません。同じ引数がstringとにも適用されintます。
ジョンパーディ

10

bool値が意味をなすように変数の名前を変更します。

名前があいまいであるため、関数に引数を説明するコメントを追加するよりも数百倍も優れています。


3
それは質問に答えません。メソッドが定義され、同じファイル内で4行離れて呼び出されると、すべてが明確になります。しかし、現時点で表示されているのが発信者だけだとしたらどうでしょう?複数のブール値がある場合はどうなりますか?単純なインラインコメントが役立つ場合があります。
ブランドン

@Brandonこれは、ブール値のdoNotPersist(または、より良いのはPersist)の呼び出しに対する議論ではありません。何を忘れるべきかを言わずに「忘れる」と言うのは、率直に言って役に立たない。ああ、オプションとしていくつかのブール値をとる方法は、高い天国に悪臭を放ちます。
-itsbruce

5

よりわかりやすい名前でローカルブール値を作成し、それに値を割り当てます。そうすれば、意味がより明確になります。

void ourFunction() {
    bool takeAction = false;  /* false means to forget */
    someFunction( takeAction );
}    

変数の名前を変更できない場合は、コメントをもう少し表現的にする必要があります。

void ourFunction() {
    /* false means that the method should forget what was requested */
    someFunction( false );
}    

1
間違いなく良いアドバイスですが、/* forget */コメントが対処すべき問題があるとは考えていません。つまり、目の前に関数宣言がなければ、何に設定されているのかを覚えるのは難しいかもしれませんfalse。(列挙型を追加する@Esailijaのアドバイスの方が優れていると思う理由と、名前付きパラメーターを許可する言語が好きな理由です。)
ロボットを

@StevenBurnap-ありがとう!私の古い答えがOPの質問に対処するのに十分明確ではなかったという点であなたは正しい。より明確にするために編集しました。

3

Qt-Style APIについて言及しているこの正確な状況について言及している良い記事があります。そこでは、ブールパラメータトラップと呼ばれますと一見の価値があります。

その要点は次のとおりです。

  1. boolが必要ないように関数をオーバーロードする方が良い
  2. Esailijaが示唆するように、enumを使用するのが最善です

2

これは奇妙なコメントです。

コンパイラの観点からsomeFunction(false /* forget */);は、実際にはsomeFunction(false);(コメントは取り除かれます)。したがって、その行はsomeFunction、最初の(そして唯一の)引数を設定して呼び出すだけです。falseです。

/* forget */パラメータの名前です。おそらく、それは簡単な(そして汚い)リマインダーに過ぎず、実際にそこにいる必要はありません。あいまいさの少ないパラメーター名を使用するだけで、コメントはまったく必要ありません。


1

Cleanコードのアドバイスの1つは、不要なコメント1の数を最小限に抑え(腐敗する傾向があるため)、関数とメソッドに適切な名前を付けることです。

その後、コメントを削除します。結局のところ、最新のIDE(Eclipseなど)は、関数の上にマウスを置くとコードでボックスをポップします。コードを見ると、あいまいさが解消されます。


1いくつかの複雑なコードにコメントすることは問題ありません。


btw誰がこのようなことを言ったのですか:「最悪のプログラマーの問題は、変数に名前を付け、1つずつオフセットする方法です」。
BЈовић

4
そのソースとしてmartinfowler.com/bliki/TwoHardThings.htmlを探している可能性があります。「コンピューターサイエンスには、キャッシュの無効化、名前の付け方、1つのエラーによるオフの2つの難しいことしかありません」に微調整を聞いたことがあります。

1

明らかなことを誤解するために、コメントは嘘をつくことができます。したがって、説明するためのコメントに頼らずに、コードを自己文書化する方が常に優れています。なぜなら、一部の人(おそらくあなた)がコメントに変更truefalseて更新しないからです。

APIを変更できない場合は、2つのオプションを使用します

  • コードに関係なく、常にtrueになるようにコメントを変更します。これを一度だけ呼び出す場合、ドキュメントをローカルに保持するため、これは良い解決策です。
     someFunction(false / * true = forget、false = remember * /); `
  • 特に#definesを複数回呼び出す場合は使用してください。
     #define FORGET true
     #define REMEMBER false
     someFunction(REMEMBER);

1

はコメントを常に真にすることについての答えが好きですが、良いのですが、このコードの根本的な問題を見逃していると思います-それはリテラルで呼び出されています。

メソッドを呼び出すときはリテラルを使用しないでください。ローカル変数、オプションのパラメーター、名前付きパラメーター、列挙型-それらを避ける最善の方法は、言語と利用可能なものに依存しますが、それらを避けるようにしてください。リテラルには値がありますが、意味はありません。


-1

C#では、名前付きパラメーターを使用してこれを明確にしました

someFunction(forget: false);

またはenum

enum Memory { Remember, Forget };

someFunction(Memory.Forget);

またはオーバーロード:

someFunctionForget();

またはポリモーフィズム `:

var foo = new Elephantine();

foo.someFunction();

-2

命名は、ブール値のあいまいさを常に解決する必要があります。私は常にブール値に「isThis」や「shouldDoThat」のような名前を付けます。例えば:

void printTree(Tree tree, bool shouldPrintPretty){ ... }

等々。ただし、他の人のコードを参照する場合は、値を渡すときにコメントを残すのが最善です。


これは質問にどう答えますか?
gnat

@gnat私は、ブールパラメータを使用してあいまいさを解決するという彼の質問に答えていました。たぶん私は彼の質問を間違って読んだかもしれません。
dchhetri
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.