20

私は優れたクリーンコードを読んでいる最中です

1 つの議論は、null をメソッドに渡すことに関するものです。

public class MetricsCalculator {
    public double xProjection(Point p1, Point p2) {
        return (p2.x - p1.x) * 1.5;
    }
}
...
calculator.xProjection(null, new Point(12,13));

これは、これを処理するさまざまな方法を表しています。

public double xProjection(Point p1, Point p2) {
    if (p1 == null || p2 == null) {
        throw new IllegalArgumentException("Invalid argument for xProjection");
    }
    return (p2.x - p1.x) * 1.5;
}

public double xProjection(Point p1, Point p2) {
    assert p1 != null : "p1 should not be null";
    assert p2 != null : "p2 should not be null";
    return (p2.x - p1.x) * 1.5;
}

私はアサーションアプローチを好みますが、アサーションがデフォルトでオフになっているという事実は好きではありません。

この本は最後に次のように述べています。

ほとんどのプログラミング言語では、呼び出し元から誤って渡された null を処理する適切な方法はありません。これが事実であるため、合理的なアプローチは、デフォルトで null を渡すことを禁止することです。

この制限をどのように強制するかについては、実際には入りませんか?

どちらにしても、強い意見を持っている人はいますか。

4

16 に答える 16

8

一般的なルールは、メソッドが引数を予期しない場合は、System.ArgumentNullExceptionnullをスローする必要があるということです。適切にスローすると、リソースの破損やその他の悪いことから保護されるだけでなく、コードのユーザーがコードのデバッグに費やす時間を節約するためのガイドとして役立ちます。Exception

防御的プログラミングに関する記事もお読みください

于 2008-08-28T13:45:29.040 に答える
4

また、すぐには使用できませんが、Spec# の言及に関連しています...Java の将来のバージョンに「null-safe 型」を追加する提案があります: 「Enhanced null handling - Null-safe types」

提案の下では、あなたの方法は次のようになります

public class MetricsCalculator {
    public double xProjection(#Point p1, #Point p2) {
        return (p2.x - p1.x) * 1.5;
    }
}

ここで、 は type のオブジェクトへ#Pointの非参照の型です。nullPoint

于 2008-08-28T16:20:36.127 に答える
4

ここでは、アサーションの使用と例外のスローの両方が有効なアプローチです。どちらのメカニズムも、ここでのケースのように、ランタイム エラーではなく、プログラミング エラーを示すために使用できます。

  • アサーションは通常、本番システムでは無効になっているため、パフォーマンスの点で有利です。
  • チェックは常に実行されるため、例外には安全性の利点があります。

どちらを選択するかは、プロジェクトの開発慣行に大きく依存します。プロジェクト全体でアサーション ポリシーを決定する必要があります。すべての開発中にアサーションを有効にすることを選択した場合は、アサーションを使用してこの種の無効なパラメーターをチェックするとよいでしょう。いずれにせよ、プログラミング エラーをキャッチして意味のある方法で処理できる可能性は低いため、アサーションのように動作します。

ただし、実際には、必要に応じてアサーションが有効になることを信頼せず、NullPointerException をスローする安全性を選択する多くの開発者を知っています。

もちろん、コードにポリシーを適用できない場合 (たとえば、ライブラリを作成していて、他の開発者がコードを実行する方法に依存している場合)、それらに対して NullPointerException をスローする安全なアプローチを選択する必要があります。ライブラリの API の一部であるメソッド。

于 2008-08-28T22:26:27.197 に答える
3

この制限をどのように強制するかについては、実際には入りませんか?

null が渡された場合は、 ArgumentExceexceptionをスローして強制します。

if (p1 == null || p2 == null) {
    throw new IllegalArgumentException("Invalid argument for xProjection");
}
于 2008-08-28T13:42:07.863 に答える
3

ほとんどのプログラミング言語では、呼び出し元から誤って渡された null を処理する適切な方法はありません。これが事実であるため、合理的なアプローチは、デフォルトで null を渡すことを禁止することです。

これまでのところ、 JetBrains@Nullableと注釈の@NotNullアプローチが最も独創的であることがわかりました。残念ながらIDE固有ですが、本当にクリーンで強力なIMOです。

http://www.jetbrains.com/idea/documentation/howto.html

これ(または同様のもの)をJava標準として持つことは本当に素晴らしいことです。

于 2008-08-28T17:51:58.263 に答える
2

Spec# は非常に興味深いですね。

そのようなものが利用できない場合、私は通常、実行時の null チェックと内部メソッドのアサーションを使用して非プライベート メソッドをテストします。null チェックを各メソッドで明示的にコーディングするのではなく、check null メソッドを使用してユーティリティ クラスに委譲します。

/**
 * Checks to see if an object is null, and if so 
 * generates an IllegalArgumentException with a fitting message.
 * 
 * @param o The object to check against null.
 * @param name The name of the object, used to format the exception message
 *
 * @throws IllegalArgumentException if o is null.
 */
public static void checkNull(Object o, String name) 
    throws IllegalArgumentException {
   if (null == o)
      throw new IllegalArgumentException(name + " must not be null");
}

public static void checkNull(Object o) throws IllegalArgumentException {
   checkNull(o, "object");
} 

// untested:
public static void checkNull(Object... os) throws IllegalArgumentException {
   for(Object o in os) checkNull(o);  
}

次に、チェックは次のようになります。

public void someFun(String val1, String val2) throws IllegalArgumentException {
   ExceptionUtilities.checkNull(val1, "val1");
   ExceptionUtilities.checkNull(val2, "val2");

   /** alternatively:
   ExceptionUtilities.checkNull(val1, val2);
   **/

   /** ... **/
} 

これは、エディター マクロまたはコード処理スクリプトで追加できます。 編集:詳細チェックもこの方法で追加できますが、1 行の追加を自動化する方がはるかに簡単だと思います。

于 2008-08-28T16:05:14.757 に答える
2

私はアサーションの使用を好みます。

public および protected メソッドでのみアサーションを使用するというルールがあります。これは、呼び出し元のメソッドが有効な引数をプライベート メソッドに渡すことを保証する必要があると考えているためです。

于 2008-08-28T13:45:10.460 に答える
1

オフトピックが話題になっているように見えるので、Scala はこれに対して興味深いアプローチをとっています。null である可能性があることを示すために で明示的にラップしない限り、すべての型は null ではないと見なされOptionます。そう:

//  allocate null
var name : Option[String]
name = None

//  allocate a value
name = Any["Hello"]

//  print the value if we can
name match {
  Any[x] => print x
  _ => print "Nothing at all"
}
于 2008-10-29T22:43:20.370 に答える
1

厳密には関係ありませんが、Spec#を参照してください。

まだ (Microsoft によって) 開発中であると思いますが、いくつかの CTP が利用可能であり、有望に見えます。基本的に、これを行うことができます:

  public static int Divide(int x, int y)
    requires y != 0 otherwise ArgumentException; 
  {
  }

また

  public static int Subtract(int x, int y)
    requires x > y;
    ensures result > y;
  {
    return x - y;
  } 

また、Notnull 型などの別の機能も提供します。.NET Framework 2.0 の上に構築されており、完全な互換性があります。ご覧のとおり、構文は C# です。

于 2008-08-28T14:22:28.220 に答える
1

少し話題から外れますが、私が非常に便利だと思うfindbugsの機能の 1 つは、メソッドのパラメーターに注釈を付けて、どのパラメーターに null 値を渡してはならないかを説明できることです。

次に、コードの静的分析を使用して、findbugsはメソッドが null 値で呼び出される可能性のある場所を指摘できます。

これには 2 つの利点があります。

  1. 注釈は、メソッドの呼び出し方法に関する意図を説明し、ドキュメントを支援します
  2. FindBugs は、メソッドの潜在的な問題の呼び出し元を指すことができるため、潜在的なバグを追跡できます。

メソッドを呼び出すコードにアクセスできる場合にのみ役立ちますが、通常はそうです。

于 2008-08-28T22:34:06.913 に答える
1

処理が遅くなるだけなので、私は通常、どちらも実行しないことを好みます。いずれにせよ NullPointerExceptions は後でスローされるため、ユーザーはメソッドに null を渡していることにすぐに気付きます。以前はチェックしていましたが、コードの 40% がコードをチェックすることになり、その時点で、素晴らしいアサーション メッセージを表示する価値がないと判断しました。

于 2008-08-28T13:38:50.337 に答える
1

wvdschel の投稿に賛成か反対かは、彼が具体的に何を言っているのかによります。

この場合、確かに、このメソッドはクラッシュするnullため、ここでの明示的なチェックはおそらく必要ありません。

ただし、メソッドが渡されたデータを単に保存し、後でそれを処理する別のメソッドを呼び出す場合は、できるだけ早く不正な入力を発見することが、バグをより迅速に修正するための鍵となります。その後、さまざまな方法で誤ったデータがクラスに渡される可能性があります。ネズミがどのようにして家に侵入したのかを突き止めようとしているようなもので、どこかで穴を見つけようとしています。

于 2008-08-28T13:43:06.113 に答える
1

@Chris Karcher私は絶対に正しいと思います。私が言う唯一のことは、パラメーターを個別にチェックし、ヌルがどこから来ているかを追跡するのがはるかに簡単になるため、ヌルだったパラメーターを例外に報告させることです。

@wvdschel うわー!コードを書くのが大変な場合は、アセンブリを後処理してパラメータ チェックを挿入できるPostSharp (または、利用可能な場合は同等の Java) などを検討する必要があります。

于 2008-08-28T13:45:56.467 に答える
0

メソッドの先頭で C#ArgumentExceptionまたは Java を投げるIllegalArgumentExceptionことは、私には最も明確な解決策のように見えます。

実行時例外 (メソッド シグネチャで宣言されていない例外) には常に注意する必要があります。コンパイラはこれらをキャッチすることを強制しないため、忘れがちです。ソフトウェアが突然停止するのを防ぐために、ある種の「キャッチオール」例外処理があることを確認してください。これは、ユーザー エクスペリエンスの最も重要な部分です。

于 2008-08-28T16:53:18.263 に答える
0

これを処理する最善の方法は、例外を使用することです。最終的に、アサーションはエンド ユーザーに同様のエクスペリエンスを提供することになりますが、エンド ユーザーに例外を表示する前に、コードを呼び出す開発者が状況を処理する方法を提供しません。Ultimatley 氏は、無効な入力をできるだけ早く (特に一般向けのコードで) テストし、呼び出し元のコードがキャッチできる適切な例外を提供することを保証したいと考えています。

于 2008-08-28T16:59:05.923 に答える
0

Java の方法では、null がプログラミング エラー (つまり、テスト フェーズの外に出てはならない) に起因すると仮定して、システムがそれをスローしたままにするか、その時点に到達する副作用がある場合は、最初に null をチェックし、 IllegalArgumentException または NullPointerException のいずれかをスローします。

null が実際の例外ケースから発生する可能性があるが、そのためにチェック済み例外を使用したくない場合は、メソッドの先頭で IllegalArgumentException ルートに移動する必要があります。

于 2008-08-28T23:55:19.217 に答える