web-dev-qa-db-ja.com

コンストラクターで試す/キャッチする-推奨プラクティス?

いつも気になっていたもの

public class FileDataValidator {

private String[] lineData;

public FileDataValidator(String[] lineData){

    this.lineData = lineData;
    removeLeadingAndTrailingQuotes();

    try
    {
        validateName();
        validateAge();
        validateTown();
    }
    catch(InvalidFormatException e)
    {
        e.printStackTrace();
    }

}

//validation methods below all throwing InvalidFormatException

コンストラクター内にtry/catchブロックを含めることはお勧めできませんか?コンストラクタに例外を呼び出し元にスローさせることができることはわかっています。私がコンストラクタで行ったようなメソッドの呼び出しで、皆さんは何を好みますか?呼び出し側のクラスで、FileDataValidatorのインスタンスを作成し、そのインスタンスでそこでメソッドを呼び出すことを好みますか?フィードバックを聞きたいだけです!

24
deanmau5

表示するコードでは、検証の問題は、このオブジェクトインスタンスを作成しているコードには伝達されません。それはおそらく良いことではありません。

バリエーション1:

メソッド/コンストラクター内で例外をキャッチした場合は、必ず呼び出し元に何かを渡してください。すべてが機能する場合にtrueに設定されるフィールドisValidを配置できます。これは次のようになります。

_private boolean isValid = false;

public FileDataValidator(String[] lineData){

    this.lineData = lineData;
    removeLeadingAndTrailingQuotes();

    try
    {
        validateName();
        validateAge();
        validateTown();
        isValid = true;
    }
    catch(InvalidFormatException e)
    {
        isValid = false;
    }
}

public boolean isValid() {
    return isValid;
}
_

バリエーション2:

または、例外またはその他の例外を呼び出し元に伝播させることもできます。私はそれをチェックされていない例外として示しましたが、あなたの例外処理の宗教に従って機能するものは何でもします:

_public FileDataValidator(String[] lineData){

    this.lineData = lineData;
    removeLeadingAndTrailingQuotes();

    try
    {
        validateName();
        validateAge();
        validateTown();
    }
    catch(InvalidFormatException e)
    {
        throw new com.myco.myapp.errors.InvalidDataException(e.getMessage());
    }

}
_

バリエーション3:

私が言及したい3番目の方法はこのようなコードを持っています。呼び出しコードでは、コンストラクターを呼び出してから、機能するかどうかに関係なくbuild()関数を呼び出す必要があります。

_String[] lineData = readLineData();
FileDataValidator onePerson = new FileDataValidator();
try {
    onePerson.build(lineData);
} catch (InvalidDataException e) {
    // What to do it its bad?
}
_

これがクラスコードです:

_public FileDataValidator() {
    // maybe you need some code in here, maybe not
}

public void build(String[] lineData){

    this.lineData = lineData;
    removeLeadingAndTrailingQuotes();

    try
    {
        validateName();
        validateAge();
        validateTown();
    }
    catch(InvalidFormatException e)
    {
        throw new com.myco.myapp.errors.InvalidDataException(e.getMessage());
    }

}
_

もちろん、build()関数はisValid()メソッドを使用して、ビルド関数にとって正しいが例外が正しい方法であるかどうかを確認できます。

バリエーション4:

私が言及したい4番目の方法は、私が一番好きなものです。このようなコードがあります。呼び出しコードでは、コンストラクターを呼び出してから、機能するかどうかに関係なくbuild()関数を呼び出す必要があります。

この種の方法は、JaxBとJaxRSが機能する方法に従っています。

  1. データの外部ソース-ファイルがあり、XMLまたはJSON形式の着信メッセージがあります。
  2. オブジェクトを構築するためのコード-コードがあり、さまざまなJSRの仕様に従って機能するコードのライブラリがあります。
  3. 検証はオブジェクトの構築とは関係ありません。

呼び出しコード:

_String[] lineData = readLineData();
Person onePerson = new Person();
FileDataUtilities util = new FileDataUtilities();
try {
    util.build(onePerson, lineData);
    util.validate(onePerson);
} catch (InvalidDataException e) {
    // What to do it its bad?
}
_

データが存在するクラスコードは次のとおりです。

_public class Person {
    private Name name;
    private Age age;
    private Town town;
... lots more stuff here ...
}
_

そして、構築および検証するユーティリティコード:

_public FileDataValidator() {
    // maybe you need some code in here, maybe not
}

public void build(Person person, String[] lineData){

    this.lineData = lineData;
    removeLeadingAndTrailingQuotes();
    setNameFromData(person);
    setAgeFromData(person);
    setTownFromData(person);
}

public boolean validate(Person person) {

    try
    {
        validateName(person);
        validateAge(person);
        validateTown(person);
        return true;
    }
    catch(InvalidFormatException e)
    {
        throw new com.myco.myapp.errors.InvalidDataException(e.getMessage());
    }

}
_
22
Lee Meador

静的なファクトリパターンを考慮する必要があります。すべての引数のコンストラクターをプライベートにします。静的FileDataValidator(args ...)メソッドを提供します。これは、すべての引数を受け入れて検証します。すべて問題なければ、プライベートコンストラクタを呼び出して、新しく作成されたオブジェクトを返すことができます。何かが失敗した場合は、例外をスローして、不正な値を提供したことを呼び出し元に通知します。

これについても言及する必要があります:catch(Exception e){printSomeThing(e); }

例外で実行できる最も危険なアンチパターンです。はい、コマンドラインでいくつかのエラー値を読み取ることができますか?呼び出し元(不正な値を提供した)は、不正な値を通知されず、プログラムの実行が続行されます。

3
gyorgyabraham

私の好みは、例外を処理する方法を知っているコードのビットによって例外を処理することです。この場合、FileDataValidatorを作成するコードのビットが、ファイルデータが無効な場合に何が起こるかを知っていると想定し、例外をそこで処理する必要があります(私は呼び出し側に伝達することを提唱しています)。

ベストプラクティスについて説明している間、クラス名FileDataValidatorの匂いがします。作成しているオブジェクトにファイルデータが格納されている場合、それをFileDataと呼びます。ファイルデータのみを検証する場合は、静的メソッドで十分です。

2
selig