「役に立たないブレース」を取り除くことを愛するボブおじさんに挑戦したことがありますか?


94

私は有料のコンテンツを参照するのは嫌いですが、このビデオはまさに私が話していることを示しています。ロバート・マーティンでの正確な12分間はこれを見ています。

いくつかのコード

そして、彼がこれを次のように変えると、「私がやるべきことの1つは、役に立たないブレースを取り除くことです」と言います。

より多くのコード

昔、遠く離れた教育で、そうではないことによって制御されていると考える別のインデントされた行を追加することでバグを簡単に導入できるため、これを行わないように教えられましたif

ボブおじさんに公平を期すために、彼は長いJavaメソッドを、はるかに読みやすい小さな機能にリファクタリングしています。彼がそれを変更し終えると、(22.18)このようになります:

さらに多くのコード

それが中括弧の削除を検証することになっているのかどうか疑問に思っています。私はすでにベストプラクティスに精通してます。この点でボブおじさんに挑戦することはできますか?ボブおじさんはその考えを擁護しましたか?


6
私はこの質問をトピック外として閉じることを投票しています。これに対する答えはすべて「いいえ」または「はい」であり、ここに引用があります。
Blrfl

35
それに数年を与えれば、多分彼は役に立たない改行も削除するでしょう、そして「if(see(something))say(something);」実際はコード行になります;
スティーブジェソップ

8
@SteveJessop私は何年も先だと聞いてうれしいです。:D改行がなければ、悪名高いバグのリスクはありません。必要に応じてブレースを追加/削除するとオーバーヘッドが増加しますが、読み取り時にさらに多くのデータが保存されます。
-maaartinus

9
ボブおじさんは、35ページの「クリーンコード」でそれ行うためのいくつかの良い点挙げいますif。さらに行を追加する必要がある場合は、ifブロックを1行に戻す別の関数(関数呼び出し)にする必要があります。あなたがそれに固執するなら、ブレースは単に問題ではありません-まったく

13
彼はreturn (page==null) ? "" : includePage(mode,page);そんなに簡潔になりたいなら、やるべきです...私はプロのアプリの開発を開始するまで、ノーブレースのスタイルはクールだと思っていました差分ノイズ、入力ミスの可能性など。中かっこは常に存在しているため、後で導入するために必要な時間とオーバーヘッドを節約できます。
vaxquis

回答:


47

読みやすさは決して小さくありません。

単一のメソッドを囲むブレースについては、私は心が混じっています。私は、単一行のreturnステートメントのようなもののためにそれらを個人的に削除しましたが、そのようなブレースを除外することは、実際、私が働いた最後の場所で私たちを非常に激しく噛みました。誰かifが必要なブレースも追加せずにステートメントにコード行を追加しました。これはCであるため、警告なしにシステムをクラッシュさせました。

その小さな大失敗の後、常に中括弧を使用することについて宗教的な人に挑戦することはありません。

したがって、読みやすさのメリットはわかりますが、これらのブレースを省略した場合に発生する可能性のある問題を強く認識しています。

私は研究や誰かの公表された意見を見つけようとすることを気にしません。誰もが1つ(つまり、意見)を持っています。それは文体的な問題であるため、1つの意見は他の意見とほぼ同じです。自分で問題について考え、長所と短所を評価し、あなた自身の気の毒な心を作り上げてください。あなたが働いている店がこの問題をカバーするコーディング標準を持っているなら、それに従ってください。


7
インデントがオフで誤解を招きやすいので、私も最近これに噛まれました。
アンディ

12
GCC 6のない-Wmisleading-indentation Cだったからです。
wchargin

2
@CandiedOrange:確立された教義に挑戦しているのはボブおじさんです。
ロバートハーベイ

1
@RobertHarveyそれを明確にしなかったのですか?はい、彼はドグマに挑戦しています。彼は私の教義に挑戦しています。私はただ良い生徒になろうとしているだけで、少なくともそれを考えています。しかし、ボブおじさんはとてもカリスマ的で、客観性を失っていないのだろうかと思います。だから私はクーレイドを飲む前に解毒剤を見つけるために助けを求めています。
candied_orange

1
@CandiedOrange:それは絶対にコンテキストのことです。ドグマを盲目的に受け入れただけの人は文脈を考慮しません。管理された言語で「ワンリターンオンリー」ルールをまだ使用しているのは、ルールがその有用性を超えて実際に障害になった後です。
ロバートハーベイ

133

ここまたはここ、または自転車の小屋が塗装されている場所で、公開されているいくつかのプロモーションまたはノーブレーススタイルの拒否を見つけることができます。

離れて自転車小屋からのステッピング、2014年の偉大なOS X / iOSのSSLのバグを覚えていますか?

if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
    goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
    goto fail;
    goto fail;
if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
    goto fail;

うん、ノーブレースブロックによって「引き起こされた」https://www.imperialviolet.org/2014/02/22/applebug.html

設定はブレースのスタイルに依存する場合があります。書かなければならなかった場合

if (suiteSetup)
{
    includePage(mode, suiteSetup);
}

私もスペースを節約したいと思うかもしれません。しかし

if (suiteSetup) {
    includePage(mode, suiteSetup);
}

「余分な」行を1つだけ使用します。


私はあなたが尋ねなかったことを知っています、しかし私が一人で働いているならば、私は異端者です。中かっこを削除する場合、私は好む

if (suiteSetup != null) includePage(mode, suiteSetup);

iOSのSSLスタイルのバグと同じ視覚的な問題はありませんが、ボブおじさんよりも多くの「不必要な」行を節約します;)慣れている場合は読みやすく、Ruby(includePage(mode, suiteSetup) unless suiteSetup.nil?)で主流に使用されています。まあ、多くの意見があることを知ってください。


13
さらに、を使用する場合} else[if(...)] {、追加の行は追加されないため、合計は1行のみになります。
Random832

12
iOS SSLバグを報告してくれてうれしいです。それ以来、このような状況でブレースを使用することが増えてきました。
ザックリプトン

4
「goto fail」バグに関しては、ボブおじさんのコーディングスタイルの他の側面もそれを防いでいたことに注目する価値があります。最も重要なのは、非常に短い方法に対する彼の強い選好です。彼はそもそも79行のメソッドを決して許容しなかったでしょうし、メソッドがより小さなものに分割されていたならおそらく、到達不能なコードに対するコンパイラの警告が生成され、問題を防ぐはずだったはずです。
ジュール

16
「goto fail」は、1行のブロックではなく、ずさんなプログラミング、さらにはコードレビューがずさんで、コードを手動で1回も実行しなかったことが原因でした。
gnasher729

35
まあ、ブレースの欠如はそのバグを引き起こしませんでした。ぞっとするようなプログラマー(および意味のあるコードレビュー、テストの欠如)が行いました。他の人の手を縛るのではなく、責任者を解雇して問題を解決してください!
オービットのライトネスレース

62

ボブおじさんは、「常にブレースを使用する」が一般的な知恵であったとき、それほど一般的ではなかったそのような間違いに対する多くの防御層を持っています:

  1. 単一行の条件式に対する強い個人的な好みなので、複数行の条件式は際立っており、追加の精査を受けます。
  2. 2行目を挿入すると自動的にインデントされるエディター。
  3. 彼が常時実行する単体テストの完全なスイート。
  4. 統合テストの完全なスイート。
  5. 彼のコードがマージされる前に行われたピアレビュー。
  6. 継続的統合サーバーによるすべてのテストの再実行。
  7. 製品品質部門によるテスト。

もし誰かがボブおじさんにチャレンジを公開したなら、彼は上記の点についてかなり良い反論をするだろうと思います。これは、早期に発見する最も簡単な間違いの1つです。


16
黙って失敗することを恐れるようなクラッシュを恐れたことはありません。少なくとも、クラッシュがいつ起こるか知っています。このようなものを見ると、私の頭の後ろがうずく。私が見while(true);{break;}たときと同じようにチクチクします。このための有効なコンテキストが本当にある場合、それを乗り越えるのに時間がかかります。
candied_orange

9
includePage()複数のステートメントを含む、ひどく記述されたマクロではない自動チェック方法がありますか?
マーティンヨーク

51
@LokiAstariはい、「Javaにはマクロがありません」と呼ばれています。
svick

11
上記のすべては、実際には「「役に立たないブレースポリシー」を使用する理由」よりも「「役に立たないブレース」ポリシーに関連する没落を回避する方法」です。あなたがそれらを必要とするという事実(特に#1)は、利点よりも不利な点と考えるべきです。
SJuan76

16
@Julesそうそう!職場で受け取ったりリリースしたりした製品はすべて(少なくとも)100%のテストカバレッジがあり、ピアレビュー(数回)があり、周辺のすべてのビジネスにはソフトウェアQA部門があります。はい。すべてのビジネスには単一のプログラマーではなくプログラマーのチームがあり、彼らがやりたいすべてのテストを行うのに十分な時間を彼らに与えているからです。はい、それはすべての職場を完璧に説明しています。私たちのソフトウェアが常に100%バグフリーで出荷されていると確信しているのに、なぜいくつかのバグを追加することを心配する必要があるのですか?
SJuan76

31

ほとんどの場合、これは個人的な好みですが、考慮すべきことがいくつかあります。

考えられるバグ

アドインする括弧を忘れ起因するバグが、私がしてきたことから、稀であると主張することができますが見られる ことを 、彼らは ない 起こる 時折(有名忘れないようにIOSの後藤が失敗するバグを)。ですから、これはあなたのコードスタイルを考慮するときの要因になるべきだと思います(一部のツールは誤解を招くようなインデントについて警告するので、ツールチェーンにも依存します)

有効なコード(それはそれはのように読み込むかもしれないバグ)

プロジェクトがそのようなバグに悩まされていないと仮定しても、コードを読むと、バグのように見えるコードのブロックが表示される場合がありますが、そうではありません。

まずは:

if (foo)
    bar();

開発者が便利なコメントを追加します。

if (foo)
    // At this point we know foo is valid.
    bar();

その後、開発者がそれに展開します。

if (foo)
    // At this point we know foo is valid.
    // This never fails but is too slow even for debug, so keep disabled.
    // assert(is_valid(foo));
    bar();

または、ネストされたブロックを追加します。

if (foo)
    while (i--) {
        bar(i);
        baz(i);
    }

または、マクロを使用します。

if (foo)
    SOME_MACRO();

「...マクロは複数行のコードを定義する可能性があるため、マクロdo {...} while (0)は複数行に使用しますか?それは、スタイルガイドにあるはずですが、念のために確認した方が良いでしょう!」


上記の例はすべて有効なコードですが、コードブロックのコンテンツが多いほど、間違いがないことを確認するためにより多くの情報を読む必要があります。

あなたのコードスタイルは、複数行のブロックが中括弧を必要とすることを定義しているかもしれません(たとえコードでなくても、実稼働コードにこれらの種類のコメントが追加されているのを見てきました。あなたがそれを読んだとき、最後にそれらの行を編集した人がブレースを追加するのを忘れたという若干の疑いがあります

差分ノイズ

単一行にブレースを使用する実用的な理由の1つは、差分ノイズを減らすことです。

つまり、変更:

if (foo)
    bar();

に:

if (foo) {
    bar();
    baz();
}

...条件付きの行が変更されるとdiffに表示されます。これにより、小さいが不要なオーバーヘッドが追加されます。

  • コードレビューでは行が変更されているように表示されます。差分ツールが単語ベースの場合、ブレースのみが変更されたことが簡単にわかりますが、行がまったく変更されていないかどうかを確認するには時間がかかります
    とはいえ、すべてのツールが単語ベースの差分をサポートしているわけではないため、差分(svn、git、hg ...など)は、派手なツールであっても、行全体が変更されたかのように表示されます。 -based diffで変更内容を確認します。
  • 注釈ツール(などgit blame)は変更された行を表示し、行の原点を追跡して実際の変更を見つけるステップを増やします。

これらはどちらも小さく、変更されたコード行をコミットするコードレビューまたは追跡に費やす時間に依存します。

diffで余分な行を変更することのより具体的な不便さは、コードの変更が競合を引き起こし、マージして手動で解決する必要があるという可能性が高いことです。


これには例外があり{ます。独自の行にあるコードベースの場合は問題ありません。

差分ノイズあなたはこのスタイルで書く場合は引数が保持していません。

if (foo)
{
    bar();
    baz();
}

ただし、これはそのような一般的な規則ではないため、主に完全性の答えに追加します(プロジェクトがこのスタイルを使用することを示唆していない)


8
私は、定義されたプラスであるコメントされた差分ノイズを減らすことが好きです。
マーティンヨーク

2
JSONに夢中な人だけがdiffノイズを考慮していたら!私はあなたを見ている、最後のコンマ規則。
ローマンスターコフ16年

1
ええと、私は{-on-its-own-lineスタイルのdiff-noiseの利点を考慮していませんでした。私はそのスタイルが本当に嫌いで、そのスタイルが必要なプロジェクトに取り組むことを嫌います。おそらくそれを念頭に置いて、そのようにコーディングしなければならないのは少しイライラするでしょう。コード密度は重要です。モニターのすべての機能を一度に見ることができれば、全体をより簡単に理解できます。読みやすくするために(関連するステートメントをグループ化するために)関数内のコードの別々のブロック間に空白行を残し、他の場所のスペースを無駄にせざるを得ないため、意図したブレークが弱くなります。
ピーターコーデス

1
@ peter-cordesも、{-on-its-own-lineスタイルのファンではなく、回答の完全性のためにのみ含まれています。使用するマクロを信頼することに関してdo{}while(0)、私はこれに関する問題を見つけます、あなたそれをほとんど常に信頼できるということです。問題は、事故が発生し、コードレビューによってエラーがスリップする可能性があることです...そして、そうでなければなかったバグを導入することができます。
ideasman42

2
潜在的なバグをリストしている場合、個々の行をコメントアウトする機能も別の良いことです。私は誰かが一行のifステートメントの本文を盲目的にコメントアウトすることによって引き起こされたバグを追跡していました。次のステートメントは、無条件に実行されるのではなく、条件付きで実行されました。
エルドリッチチーズ

15

数年前、私はCコードをデバッグするために連れてこられました。バグを見つけるのは大変でしたが、最終的には次のようなステートメントに要約されました。

if (debug)
   foo (4);

そして、それを書いた人がfooマクロとして定義していたことが判明しました。2行のコードを含むマクロ。そしてもちろん、これらの2行のうち最初の行のみが対象となりifます。(したがって、2行目は無条件に実行されました。)

これは、Cとそのプリプロセッサ(コンパイルの前に置換を行う)に完全に固有のものかもしれませんが、私は決して忘れていません。そのようなことはあなたにマークを残します、そしてなぜあなたはそれを安全にプレイしませんか?特にあなたがさまざまな言語とツールチェーンを使用し、そのようなシェナンガンが他の場所で不可能であることを確信できない場合はどうですか?

どうやら、かっこを他の人とは異なってインデントして使用しているようです。1行のif場合、次のようにします。

if (debug) { foo (4); }

そのため、中括弧を含めるために追加の行は必要ありません。


6
その場合、そのマクロを書いた人がだれでも責任を負います。
gnasher729

6
Cプリプロセッサマクロを正しく記述することは直感的ではないかもしれませんが、それは可能です。そしてgnasher729が指摘しているように、それは行われるべきです。そして、あなたの例は、複数のステートメントを含むすべてのマクロがdo { ... } while(0)ループに本体を含める必要がある理由です。これによりif()、ステートメントを囲むことによって常に正しく処理されるだけでなく、ステートメントの最後のセミコロンが正しく飲み込まれるようになります。そのような単純なルールを知らない人は、単にプリプロセッサマクロを書くべきではありません。呼び出し元のサイトで守ることは、守るべき間違った場所です。
cmaster

18
@cmaster:本当ですが、マクロ(または他の言語機能)が他の人によってどのように書かれているかは、私が制御することはできません。中括弧の使用方法は私の管理下にあります。これが中かっこを含める鉄の理由だとは絶対に言っていませんが、他の人の間違いから身を守るためにできることです。
ウェイン

@ gnasher729、だれか他の人のせいだと気にする人はいますか?コードのレビューを行い、いくつかの妥当なコード標準に従っている場合、それはチームのせいかもしれません。そして、それがユーザーに届くなら、彼らはバグのあるソフトウェアをリリースしている組織を非難するでしょう。
ideasman42

1
マクロを思いついた人は誰でも責任を負います。
ニーム

14

「ボブおじさん」は彼の意見を持つことができます。あなたはあなたの意見を持つことができます。彼に挑戦する必要はありません。

当局に訴えたい場合は、クリス・ラトナーを取りましょう。Swiftでは、ステートメントの括弧が失われますが、常に中括弧が付いています。議論はありません、それは言語の一部です。したがって、「Uncle Bob」がブレースの削除を開始すると、コードはコンパイルを停止します。

他の人のコードを見て、「役に立たないブレースを取り除く」ことは悪い習慣です。コードをレビューする必要がある場合、および競合が不必要に作成される場合にのみ、余分な作業が発生します。たぶん、「ボブおじさん」は、コードレビューを必要としないほど素晴らしいプログラマーでしょうか?ある最悪の場所に隠されたコードレビューなしで、あるトッププログラマーが「if(p!= NULL)」を「if(!p)」に変更したため、私は一週間を無駄にしました。

これはほとんど無害なスタイルの議論です。中括弧には、中括弧を追加せずに別のコード行を挿入できるという利点があります。ロギングステートメントまたはコメントのように(コメントが続き、その後にステートメントが続く場合はひどいです)。あたかも同じ行のステートメントには、多くのデバッガーに問題があるという実用上の欠点があります。しかし、好きなことをしてください。


1
UB(sic!)が彼の意見を事実として提示しているので、それは「挑戦する」ことだと思います-少なくとも、私が彼に耳を傾けるとき、私はそのように思います。
vaxquis

3
「好きなことをしてください」と言うことは、実際にはボブおじさんに同意することです。なぜなら、それは彼が「重要ではない」と主張しているからです。多くの言語では、これは問題ではありません。私は以前、すべての人が常に中括弧を使用する必要がある問題だと考えていましたが、それに挑戦する人はいませんでした。今、ボブおじさんがそれに挑戦していますが、彼は非常にリファクタリングされた小さな方法で働いているのか、それでいっぱいであるので、彼がそれを逃れるのだろうかと思っています。@PaulDraperが彼の答えで言及している種類のバグのために、私はそれを無害だとは考えていませんでした。
candied_orange

常に:構文ノイズが邪魔にあり、近隣にブレースのための余分な「空白」行がさらに読みやすさを妨げ、行が離れて分割すべきではない段落に視覚的な区切りを追加します。これは、あなたが言及する簡潔な小さなブロックでは特に重要です。
JDługosz

9

中括弧を削除しない理由は次のとおりです。

  1. 意思決定の疲労を軽減します。常に中括弧を使用する場合、中括弧が必要かどうかを決定する必要はありません。

  2. 開発ドラッグの削減:最終的にロジックの複数行をすべてメソッド抽出しようとしても、ブレースレスifをブレースifロジックに追加しなければならないことは、開発ドラッグの面倒なことです。そのため、中かっこを削除する努力と、さらにコードが必要なときにそれらを再び追加する努力があります。小さいが、迷惑です。


0

若い同僚は、私たちが冗長で不必要だと思っているブレースが実際に彼にとって役立つと言っています。私は正確な理由を思い出しませんが、それが彼がより良いコードをより速く書くことができるなら、それだけがそれらを保持する理由です。

FWIWは、我々は1行にそれらを置くことは、このような短い前提条件が行わないことに合意した妥協案国連私には読める/気が散ります。例えば

if (!param) { throw invalid_argument (blahblah); }
if (n < 2) { return 0; }
// real code for function starts here...

また、これらの導入の前提条件は多くの場合、ボディ(aなどreturn)の制御フローステートメントであるため、同じ条件になり、ブレースを書くのを忘れることを意図した2番目のステートメントを追加するのは不安です。条件の下での2番目のステートメントは、とにかくデッドコードであり、意味がありません。

読書の問題の流encyさは、人の脳パーサーが条件文の一部として中括弧を持つように配線されていることが原因であると思われます。これは、C ++の文法を正確に表したものではありませんが、他の特定言語を最初に学習する場合副作用になる可能性があります。


1
ボブおじさんは、おそらく彼らなしでより良いコードをより速く書くと思います。
djechlin

2
私も、C ++に堪能な人もいます。
JDługosz

1
「より良いコードをより速く書くことができるなら、それだけがそれらを保持する理由です」++私は同じことを言ったJrがいます。
ラバーダック

...そしてそれが、ボブおじさんが望んでいるようにそれをする理由だけです。
djechlin

-1

今そのビデオを見て、中かっこを削除すると、コードをリファクタリングする必要がある場所を簡単に確認できることに気付きました。中括弧が必要な場合、コードはおそらく少し簡潔になります。

とはいえ、チームにいるときは、ブレースを削除すると、いつか予期しない動作が発生する可能性があります。私はそれらを保つと言います、そしてあなたは物事がうまくいかないときあなた自身に多くの頭痛を救います。


ポイントが作られ、前10件の答えで説明した上で、これはかなりのものを提供していないようだ
ブヨ

-3

私はこれをしないように教えられました。そうしないとifによって制御されていると考える別のインデントされた行を追加することでバグを導入しやすくなります。

コードのユニットテストが適切に行われている場合、この種の「バグ」が爆発的に発生します。

役に立たないい括弧を避けることでコードをきれいにすることは、私が従う習慣です。


9
もちろん、逆も同様です。コードにバグがない場合は、単体テストは必要ありません。現実には、私たちは不完全な世界に住んでおり、コードの間違いやテストの間違いが時々あります。(私の経験では、後者は実際に一般的です。)したがって、テストは確かにバグのリスクを減らしますが、これはリスクを再び上げる他の方法を見つけるための言い訳にはなりません。
ruakh

3
ruakhのコメントに追加するには、コードを100%単体テストすることはできません。
BЈовић

私のコードを見に来てください;)TDDの練習中、この種のバグを非常に迅速に表示することは常に可能です。
Mik378

@ Mik378それは間違いなく真実ではありません。要点は、ブレースを使用した場合、心配する必要さえないということです。
GoatInTheMachine
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.