2

こんにちは、私は次のような小さな関数を書きました

public void foo(MyClassA paraA) {
    if (paraA == null) return;
    MyClassB paraB = doSomeStuff(paraA);
    if (paraB == null) return;
    MyClassC paraC = doMoreStuff(paraB);
    if (paraC == null) return;
    ....
}

上記はすぐに失敗し、読みやすくなっています (つまり、null 値を返す意図は明らかです)。しかし、単に戻るのではなく、エラーログを記録したいので、次のように変更しました

public void foo(MyClassA paraA) {
    if (paraA == null) {doLog(); return;}
    MyClassB paraB = doSomeStuff(paraA);
    if (paraB == null) {doLog(); return;}
    MyClassC paraC = doMoreStuff(paraB);
    if (paraC == null) {doLog(); return;}
    ....
}

上記もきれいで読みやすいですが、doLog() を数回繰り返す必要があります。だから私は再び

public void foo(MyClassA paraA) {
    if (paraA != null) {
        MyClassB paraB = doSomeStuff(paraA);
        if (paraB != null) {
            MyClassC paraC = doMoreStuff(paraB);
            if (paraC != null) {
                ....
                return;
            }
        }
    }
    doLog();
}

上記は doLog() を 1 回だけ呼び出しますが、非常に見苦しく読みにくい、深くネストされた if ステートメントで終了しました。では、以前と同じ清潔さを保ち、doLog() を 1 回だけ使用するにはどうすればよいでしょうか。foo() に対して void 以外のものを返すことは許可されていないことに注意してください。また、null チェックとは対照的に try/catch を使用することはアンチパターンであることも読みました。

やってみるなら、次のようなものを書きたい

public void foo(MyClassA paraA) {
    while(true) {
        if (paraA == null) break;
        MyClassB paraB = doSomeStuff(paraA);
        if (paraB == null) break;
        MyClassC paraC = doMoreStuff(paraB);
        if (paraC == null) break;
        ....
        return;
    }
    doLog();
}

上記は私のすべてのニーズを満たします (高速で失敗し、クリーンで、ネストされていない場合) が、ここで while ループを使用することはアンチパターンですか?

4

3 に答える 3

1

私見あなたの2番目のコードスニペットは、ypuがすべきことです。

コードを短くしようとしないでください。これはアンチパターンです。

if (a==null) {
  log("Failed in step a");
  return;
}
B b = a.doSomething();

読んで理解するのが非常に速いです。このコードを圧縮しても、何も節約できません。ゼロ。なだ。Hotspot VM に任せて、コードをわかりやすくすることに専念してください。"if null then log return" は、よく理解され受け入れられている古典的なパターンです。

次のようなラムダアンチパターンを使用してコードを「読み取り可能」にしようとすることが一般的になりました。

B b = ifNullLog(a, () -> a.doSomething())

どこ

T ifNullLog(Object guard, Function<T> func) {
  if (guard == null) { doLog(); return null; }
  return func.run();
}

しかし、これは完全なアンチパターンです。実際、コードを壊す危険を冒さずにそのようなログ ステートメントを簡単に挿入できるようにするために、すべての if、else、for、while に中括弧を要求することもベスト プラクティスです。

最初のスニペットのようなコード:

if (a == null) return;

危険です。Apple の SSL 災害のようなさまざまなバグを見てください 。角かっこの欠落に気付かずに誰かが doLog を追加すると、関数は常に null を返します。Apple SSL バグ (またはハートブリードでしたか?) は本質的に

if (a==null)
  return;
  return;
B b = a.doSomething();

バグがどれほど微妙であるかがわかりますか?Java コンパイラーは、到達不能なコードに関するものである場合、幸いなことに警告します。それ以外の場合は必ずしも警告するわけではありません...常にブラケットと適切にフォーマットされたコードを使用することで、そのようなバグを簡単に回避できます。aestetics ではなく、エラーを回避するためにコードをフォーマットします

リターン コードを使用することも問題ありません。成功をデフォルトにしないでください (heartbleed をもう一度参照してください)。

Code c = execute(a);
if (c != Code.SUCCESS) {
  doLog(c);
  return;
}

どこ

Code execute(A a) {
  if (a == null) { return Code.FAILED_A_NULL; }
  B b = a.doSomething();
  if (b == null) { return Code.FAILED_B_NULL; }
  ...
  return Code.SUCCESS;
}

"return" の古典的な使用例であり、もう 1 つの優れたパターンです。

于 2015-02-14T10:49:10.277 に答える
1

これはきれいだと思いますか

public void foo(MyClassA paraA) {

    MyClassB paraB = paraA != null?doSomeStuff(paraA):null;
    MyClassC paraC = paraB != null?doMoreStuff(paraB):null;

     if (paraC != null) {
         ....

     }

     doLog();
}
于 2015-02-13T07:08:04.770 に答える