6

この目標を達成するための一般的な方法は次のとおりです。

public void myContractualMethod(final String x, final Set<String> y) {
    if ((x == null) || (x.isEmpty())) {
        throw new IllegalArgumentException("x cannot be null or empty");
    }
    if (y == null) {
        throw new IllegalArgumentException("y cannot be null");
    }
    // Now I can actually start writing purposeful 
    //    code to accomplish the goal of this method

この解決策は醜いと思います。メソッドは、有効な入力パラメータコントラクトをチェックするボイラープレートコードですぐにいっぱいになり、メソッドの核心を覆い隠します。

これが私が欲しいものです:

public void myContractualMethod(@NotNull @NotEmpty final String x, @NotNull final Set<String> y) {
    // Now I have a clean method body that isn't obscured by
    //    contract checking

それらのアノテーションがJSR303/ Bean Validation Specのように見える場合、それは私がそれらを借りたためです。残念ながら、これらはこのようには機能しないようです。これらは、インスタンス変数に注釈を付けてから、バリデーターを介してオブジェクトを実行することを目的としています。

多くのJavaの契約による設計フレームワークのうち、私の「欲しい」例に最も近い機能を提供するのはどれですか?スローされる例外は、カプセル化が壊れないように、実行時例外(IllegalArgumentExceptionsなど)である必要があります。

4

8 に答える 8

7

本格的な契約による設計メカニズムをお探しの場合は、DBCのウィキペディアページにリストされているプロジェクトのいくつかをご覧ください。

ただし、もっと簡単なものを探している場合は、checkNotNull()メソッドを提供するGoogleコレクションのPreconditionsクラスを調べることができます。したがって、投稿したコードを次のように書き直すことができます。

public void myContractualMethod(final String x, final Set<String> y) {
    checkNotNull(x);
    checkArgument(!x.isEmpty());
    checkNotNull(y);
}
于 2009-11-27T14:44:56.560 に答える
3

I've seen a technique by Eric Burke that is roughly like the following. It is an elegant use of static imports. The code reads very nicely.

To get the idea, here is the Contract class. It is minimal here, but can be easily filled out as needed.

package net.codetojoy;

public class Contract {
    public static void isNotNull(Object obj) {
        if (obj == null) throw new IllegalArgumentException("illegal null");
    }
    public static void isNotEmpty(String s) {
        if (s.isEmpty()) throw new IllegalArgumentException("illegal empty string");
    }
}

And here is an example usage. The foo() method illustrates the static imports:

package net.codetojoy;

import static net.codetojoy.Contract.*;

public class Example {
    public void foo(String str) {
        isNotNull(str);
        isNotEmpty(str);
        System.out.println("this is the string: " + str);
    }

    public static void main(String[] args) {
        Example ex = new Example();
        ex.foo("");
    }
}

Note: when experimenting, note that there may be a bug around doing this in the default package. I've certainly lost brain cells trying it.

于 2009-11-27T16:18:00.790 に答える
1

Plain Java として実装された小さなJava Argument Validationパッケージがあります。いくつかの標準的なチェック/検証が付属しています。また、独自のより具体的な検証が必要な場合のために、いくつかのヘルパー メソッドが付属しています。複数回発生する検証の場合は、インターフェイス ArgumentValidation を独自のもので拡張し、クラス ArgumentValidationImpl から拡張する実装クラスを作成します。

于 2010-01-28T22:13:49.697 に答える
0

これはあなたの質問に直接答えるものではありませんが、あなたの問題の一部はあなたが検証をやり過ぎていることだと思います。たとえば、最初のテストを次のように置き換えることができます。

if (x.isEmpty()) {
    throw new IllegalArgumentException("x cannot be empty");
}

そして、Javaに依存してifisをスローしNullPointerExceptionます。「契約」を変更して、特定のタイプの「違法なパラメータで私に電話をかけた」状況でNPEがスローされるようにする必要があります。xnull

于 2009-11-27T14:47:13.827 に答える
0

Jared は、DBC のサポートを Java に追加するさまざまなフレームワークを紹介しました。
私が最も効果的であるとわかったのは、コントラクトを JavaDoc (または使用する任意のドキュメント フレームワーク。Doxygen は DBC タグをサポートしています) で単純に文書化することです。
多くのスローと引数のチェックによってコードを難読化することは、実際には役に立ちません。あなたの読者。ドキュメンテーションは。

于 2009-11-27T15:44:20.780 に答える
0

完全に機能するソリューションではありませんが、JSR-303 にはメソッド レベルの検証拡張機能の提案があります。これは単なる拡張の提案であるため、JSR-303 の実装はこれを自由に無視できます。実装を見つけるのはもう少し難しいです。Hibernate Validator はまだサポートしていないと思いますが、agimatec-validationには実験的なサポートがあると思います。私はこの目的でどちらも使用していないので、どれだけうまく機能するかわかりません。誰かが試してみたら、私は知りたいと思います。

于 2010-04-13T09:00:03.640 に答える
0

パラメーター アノテーション、リフレクション、および汎用バリデーター クラスを使用して、アプリ全体の機能を作成します。たとえば、次のようなクラス メソッドをコーディングできます。

.. myMethod( @notNull 文字列 x, @notNullorZero 文字列 y){

if (Validator.ifNotContractual(getParamDetails()) {
    raiseException..
    or 
    return ..
}

}

クラス メソッドは、コントラクト要件に注釈を付けるために「マークアップ」されています。リフレクションを使用して、パラメーター、その値、および注釈を自動的に検出します。すべてを静的クラスに送信して検証し、結果を知らせます。

于 2009-11-27T17:13:16.480 に答える
0

Java 8 を使用している場合、ラムダを使用して、検証用の非常にエレガントで読みやすいソリューションを作成できます。

public class Assert {

    public interface CheckArgument<O> {
        boolean test(O object);
    }

    public static final <O> O that(O argument, CheckArgument<O> check) {
        if (!check.test(argument))
            throw new IllegalArgumentException("Illegal argument: " + argument);
        return argument;
    }
}

次のように使用します。

public void setValue(int value) {
    this.value = Assert.that(value, arg -> arg >= 0);
}

例外は次のようになります。

Exception in thread "main" java.lang.IllegalArgumentException: Illegal argument: -7
    at com.xyz.util.Assert.that(Assert.java:13)
    at com.xyz.Main.main(Main.java:16)

最初の良い点は、上記の Assert クラスだけで本当に必要なものがすべて揃っていることです。

public void setValue(String value) {
    this.value = Assert.that(value, arg -> arg != null && !arg.trim().isEmpty());
}

public void setValue(SomeObject value) {
    this.value = Assert.that(value, arg -> arg != null && arg.someTest());
}

もちろんthat()、さまざまな方法で実装できます。フォーマット文字列と引数を使用して、他の種類の例外をスローするなどです。

ただし、さまざまなテストを実行するために実装する必要はありません。

次のような場合は、事前にパッケージ テストを実行できないわけではありません。

public static CheckArgument<Object> isNotNull = arg -> arg != null;

Assert.that(x, Assert.isNotNull);

// with a static import:

Assert.that(x, isNotNull);

これがパフォーマンスに悪いのか、他の理由で良い考えでないのか、私にはわかりません。(自分でラムダを調べ始めたところですが、コードは正常に動作しているようです...)しかし、それをAssert短く保つことができ(プロジェクトにとって重要ではない可能性のある依存関係は必要ありません)、テストが非常に見やすいことが気に入っています。

エラーメッセージを改善する方法は次のとおりです。

public static final <O> O that(O argument, CheckArgument<O> check,
    String format, Object... objects) 
{
    if (!check.test(argument))
        throw new IllegalArgumentException(
            String.format(format, objects));
    return argument;
}

あなたはそれを次のように呼びます:

public void setValue(String value) {
    this.value = Assert.that(value, 
        arg -> arg != null && arg.trim().isEmpty(), 
        "String value is empty or null: %s", value);
}

そして出てくる:

Exception in thread "main" java.lang.IllegalArgumentException: String value is empty or null: null
    at com.xyz.util.Assert.that(Assert.java:21)
    at com.xyz.Main.main(Main.java:16)

更新:パッケージ化されたテストで構造を使用する場合x = Assert...、結果はパッケージ化されたテストで使用される型にキャストされます。したがって、変数の型にキャストし直す必要があります...SomeClass x = (SomeClass) Assert.that(x, isNotNull)

于 2018-04-11T14:25:28.817 に答える