0

null「問題が見つからない」場合に a を返すことによって、高価なステートレス チェック コードを実行した結果を効果的にキャッシュするのは「悪い習慣」ですか? 利点は、コードが最小限であり、クラス/コードの肥大化がないことです。

これを次のコードで示します。

public static String getErrorMessage(SomeState x) {
    // do some "expensive" processing with "x"
    if (someProblem)
        return "Some problem";
    if (someOtherProblem)
        return "Some other problem";
    return null; // no error message means "all OK"
}

そして呼び出しコード:

String message = getErrorMessage(something);
if (message != null) {
    display(message);
    return; 
}
// proceed

nullこのパターンでは、「エラーがないため、エラー メッセージはありません」という意味に戻ることで、高価なコードを 2 回実行する必要がなくなります。また、余分な「価値の低い」クラス/コードはありません。

明らかな代替手段は、A) チェックとメッセージ作成の懸念を分離することです。

public static boolean isOK(SomeState x) {
    // do some "expensive" processing with "x"
    return thereIsNoProblem;
}

public static String getErrorMessage(SomeState x) {
    // do some "expensive" processing with "x"
    if (someProblem)
        return "Some problem";
    if (someOtherProblem)
        return "Some other problem";
}

そして呼び出しコード:

if (!isOK(something)) {
     display(getErrorMessage(something)); // expensive code called a second time here
     return;
}
// proceed

高価なコードを 1 回実行して問題があるかどうを判断し、もう一度問題の内容を判断する、または B) 「if」部分に答えるブール型フィールドと答える文字列フィールドを持つ「結果」オブジェクトを返す「メッセージ」部分、例えば

class MyResult { // like a struct in C
    boolean ok;
    String message;
    // accessor methods omitted 
}

public static MyResult verify(SomeState x) { ...}

そして呼び出しコード:

MyResult result = verify(something);
if (!result.ok) { // spare me the lecture on accessors - this is illustrative only
    display(result.message);
    return;
}

これはクラスの肥大化を引き起こし、少し不器用な私見です。

このように戻り値を「オーバーロード」するのは「悪い」ことですか?
それは確かに、私が考えることができるすべての選択肢よりも「きれい」です。


代替案を提供する場合は、null を返すことが「悪い」と考える理由を教えてください。この単純な手法を使用しないことを正当化するリスクまたは欠点を述べます。

4

7 に答える 7

1

呼び出された関数内のエラー/異常状態を報告するための限られた数のオプションがあります。

  1. 「エラー」を示す値を返します (関数が値を返すことができる/返す場合)。
  2. 関数の「エラー ポインタ」パラメータで示される位置にエラー コードを返します。
  3. 例外をスローします。
  4. 以前に定義された (またはパラメーターで指定された) 「デリゲート」または「コールバック」に制御を渡します。
  5. グローバル エラー インジケーターを更新します。
  6. グローバル エラー ルーチンを呼び出します。

これらのどれも特に魅力的ではありません.global は良くありません.delegate/callback は不器用で冗長であり、例外は遅いです. したがって、最初の 2 つは最も頻繁に使用されるオプションです。

2 では、値を返す関数からエラーを返す場合でも、値を返す必要があるという問題があります。また、オブジェクトを返す関数の場合、nil は返す最も論理的な値です。

関係なく nil を返すことになります。

于 2013-05-14T16:58:46.883 に答える
0

私はかなり反 NULL であり、個人的には、""エラーがないことを意味する空の文字列を返すか、おそらく のような定数を返します"OK"。ただし、null は許容されます。

列挙型/値オブジェクトの概念は少しやり過ぎのように感じますが、多くの場合、要件が拡大し、最終的にはそのようなものが必要になります。

複数のエラーが発生する可能性がある場合は、文字列のリストを返すのが好きです。空のリストは明らかに問題がないことを意味します。この場合、null を返さず、空のリストを返します。

于 2013-05-14T17:17:16.420 に答える
0

私は自分のコメントに取り組み、それを答えにすることにしました。これは、次のような列挙に適したコンテキストだと思います。

public enum OperationStatus {
    ERROR1 {
        @Override
        public String toString() {
            return "err1";
        }
    }, 
    ERROR2
    //The same as error 1
    //
    //Many error types here
    SUCCESS {
        @Override
        public String toString() {
            return "ok";
        }
    }
}

このようにして、メソッドは、 として取得できるコードで実際のエラーを指定して、この列挙の値を返すことができますString。これは特に、ローカライズされたバンドルを使用してそのようなエラーをローカライズすることを計画している場合に特に当てはまります。ローカライズされたバンドルを切り替えて、定義された各コードに関連付けられた値を取得できます。

一方、これは、将来エラーを追加する予定がない場合、または新しいエラー タイプが発生した場合にこの列挙に必要な変更を随時行う余裕がある場合に機能します。

列挙型を回避するための別の方法 (この配列を更新する必要はありますが、列挙型を使用することはできません) は、エラー コードとして を返し、それをインデックスとして使用Stirng[]できるため、単純な方法でもうまくいきます。intその配列の実際のコードを取得して、解釈、表示、およびローカライズできます。

于 2013-05-14T15:28:18.210 に答える
0

私は使うだろう:

public class ValidationResult {

    public final boolean isValid;

    public final String errorMessage; // may be null if isValid == true

    public ValidationResult(boolean isValid, String errorMessage) {
        if (!isValid && errorMessage == null) {
            throw new IllegalArgumentException();
        }
        this.isValid = isValid;
        this.errorMessage = errorMessage;
    }
}

それで:

public static ValidationResult validate(SomeState x) {
    // blah
    return new ValidationResult(isValidTest, errorMessageIfNot);
}

と:

ValidationResult r = validate();
if (!r.isValid) {
    display(r.errorMessage);
}

または、チェック例外をスローすることもできます。

public class ValidationException extends Exception {
    // your code here
}

それで:

public static boolean validate(SomeState x) throws ValidationException {
    // ...
    if (isValid) {
        return true;
    } else {
        throw new ValidationException(message):
    }
}
于 2013-05-14T15:16:47.330 に答える
0

あなたの最初の解決策はまったく問題ありません.nullは、戻り値の「不在」を表すのに最適です。

于 2013-05-14T15:21:47.760 に答える
0

自分で使う分には問題ありませんが、改善される可能性があります。改善できる理由について、Tony Hoareの言葉を引用させてください。

私はそれを私の10億ドルの間違いと呼んでいます。それが 1965 年のヌル参照の発明でした。その当時、私はオブジェクト指向言語 (ALGOL W) での参照のための最初の包括的な型システムを設計していました。私の目標は、参照のすべての使用が完全に安全であることを保証することであり、チェックはコンパイラによって自動的に実行されます。しかし、実装がとても簡単だったという理由だけで、null 参照を挿入したいという誘惑に抵抗できませんでした。これにより、無数のエラー、脆弱性、およびシステム クラッシュが発生し、過去 40 年間でおそらく 10 億ドルの痛みと損害が発生しました。

まず、例外を使用できます。

public static String getErrorMessage(SomeState x) throws YourException {
    // do some "expensive" processing with "x"
    if (someProblem)
        throw YourException("Some problem");
    if (someOtherProblem)
        throw YourException("Some other problem");
    return null; // no error message means "all OK"
}

次に、カスタム オブジェクトを作成できます。

class ErrorState {
  boolean hasFoundError;
  String message;

  ErrorState() {
    hasFoundError = false;
  }

  ErrorState(String message) {
    hasFoundError = true;
    this.message = message;
  }

  public final static ErrorState NO_ERROR = new ErrorState();
}

最後に、潜在的なエラーのセットが有限である場合は、を使用できますenum(私の意見では、これがより良い選択です):

enum ErrorState {
  NO_ERROR(""),
  SOME_ERROR("Some error"),
  SOME_OTHER_ERROR("Some other error");

  public final String message;

  ErrorState(String message) { this.message = message; }
}
于 2013-05-14T15:18:26.167 に答える
0

isOK() をチェックしてからエラー メッセージを取得する代わりに、適切なメッセージを含む isOK() に throws 例外を追加し、try/catch を使用してエラー メッセージを表示します。

1 つの関数がスローで十分な場合に、複数のチェック レイヤーを使用してエラーを取得する理由はないようです。

于 2013-05-14T15:18:57.590 に答える