6

私は最近、TDD とクリーン コードについてよく読んでいるので、これらを使用する単純なプロジェクトに取り組み始めました。

JavaFileオブジェクトをパラメーターとして受け取るクラスがあります。このFileオブジェクトはディレクトリである必要があり、特定のプレフィックスで始まる必要があります。私の最初のパス スルーではFile、コンストラクターを呼び出す前にオブジェクトのチェックを行いました。つまり、オブジェクトがディレクトリであることをチェックし、名前が有効であることをチェックしました。しかし、それを有効にするもの、特に有効なプレフィックスを指定しているのは呼び出し元であることが好きではありません。このロジックはクラス自体に配置する必要があると思います。

コンストラクターでこのチェックを行い、有効でない場合は例外をスローすることもできますが、問題の性質を考えると、 のリストを反復処理しているFile場合、それらの一部が「有効ではない」ことが完全に予想されます' (つまり、それらはディレクトリではなくファイルになります) では、スローすることはException本当に保証されていますか?

public MyObject(File directory) {
    if (!directory.isDirectory()) {
        throw new IllegalArgumentException("Must be a directory");
    }
    if (!directory.getName().startsWith("Prefix")) {
        throw new IllegalArgumentException("Must start with Prefix");
    }
    ....
}

Factory メソッドを追加してオブジェクトを作成し、無効な場合は null を返すことを考えましたFile

public static MyObject createMyObject(File directory) {
    if (!directory.isDirectory() || !directory.getName().startsWith("Prefix")) {
        return null;
    }
    return new MyObject(directory);
}

別の方法として、コンストラクターを呼び出す前に、呼び出し元のファイルを検証するクラスに静的メソッドを追加することを考えました。

public static boolean isValid(File directory) {
    return directory.isDirectory() && directory.getName().startsWith("Prefix");
}

if (MyObject.isValid(directory)) {
    MyObject object = new MyObject(directory);
}

では、クリーンなコードとすべての OOP 原則 (単一の責任、結合など) に関して、これを行うための好ましい方法はどれでしょうか?

アップデート:

すでに投稿された回答のいくつかを読んだ後、私の質問が実際にあったので、一般的にではなく、私の現在の状況にのみ適用できる別の可能性について考え始めました.

呼び出しコードの一部として、ファイルシステムからのパスがあり、そのディレクトリ内のすべてのファイルをリストしています。有効かどうかに関係なく、MyObject コンストラクターに渡すのは各ファイルです。listFiles が有効なディレクトリのみを返すことを保証するFileFilterメソッドに を渡すことができます。MyObject 内で宣言できますlistFilesFileFilter

public static FileFilter getFilter() {
    return new FileFilter() {
        public boolean accept(File path) {
            return path.isDirectory() && path.getName().startsWith("Prefix");
        }
    };
}

コンストラクターが例外をスローした場合、有効なディレクトリのみが渡されることが期待されるため、実際には例外的な状況になります。これを行うと、コンストラクター/ファクトリーからチェック済み例外の必要性を取り除くことができるということになります。しかし、それをコンストラクターに入れるか、ファクトリー・メソッドに入れるかという問題はまだ残っています。

4

5 に答える 5

3
public static MyObject createMyObject(File directory) throws IllegalArgumentException{
    if (!directory.isDirectory() || !directory.getName().startsWith("Prefix")) {
        return throw new IllegalArgumentException("invalid parameters")";
    }
    return new MyObject(directory);
}

これは、提案された 2 つのオプションを組み合わせたオプションの 1 つです。ファクトリ メソッドを使用し、ファクトリ メソッドが得意とする前提条件も検証します。したがって、IMOはこれが良いオプションになる可能性があります。

オプション 2 をそのまま使用しない理由:nulls例外をスローして、いくつかの前提条件が満たされていないことをユーザーに警告できる場合、 戻ることは悪いオプションであるためです。

更新: この戦略は、有効な状態のオブジェクトのみが作成されることを保証します。また、インスタンスをモック化する場合は、MyObjectランタイム ポリモーフィズムを使用するためのインターフェイスを実装し、モック オブジェクトを渡すことができます。それが理にかなっていることを願っています。

于 2013-03-11T11:38:21.657 に答える
1

検証コードがどこに属するかは、「MyObject」クラスが何を表すかによって異なると思います。

MyObjectがディレクトリではなくファイルを持っていると失敗する操作を実行する場合、コンストラクターに検証コードを含める必要があります。これにより、クラスが自己完結型になり、クラスの再利用が可能になります。後で。

MyObjectがファイル/ディレクトリの単なるコンテナであり、ディレクトリ固有のコードが含まれていない場合は、ファイルではなくディレクトリを必要とするクラス内に検証コードを配置します

于 2013-03-11T11:47:35.753 に答える
1

static boolean isValid()指定された引数が検証メソッドによって補完された契約を満たさない場合に、検証して例外をスローするコンストラクターの組み合わせを提供することをお勧めします。

ループで使用される検証メソッドは、有効なオブジェクトを構築するための優れた読みやすい方法を提供しますが、コンストラクターは、必要に応じて例外をスローすることにより、検証されていない使用が確実に処理されるようにします。

于 2013-03-11T11:53:17.883 に答える
0

これはDesign-by-Contractと呼ばれ、着信引数のチェックに役立つライブラリがあります。

于 2013-05-03T15:22:01.707 に答える
0

場合によります...

コーディングの観点から見ると、最も単純でクリーンなアプローチは、分解する (unchdcked) コンストラクターです。呼び出し元がディレクトリを渡すだけであり、渡す必要があるという合理的な期待がある場合は、それに従います。

呼び出し元が非ディレクトリを渡す可能性があるという合理的な期待がある場合は、次の 2 つの選択肢があります。

  1. 呼び出し元が状況に対処できる場合は、コンストラクターにチェック例外をスローさせます。
  2. 呼び出し元が例外を処理できない場合、呼び出し元がメソッドを呼び出した場合、クラスに「何もしない」ようにすることができます。基本的に、コンストラクターに渡されたファイルがディレクトリでない場合、何かを「実行」するすべての要求を無視します。
于 2013-03-11T12:43:47.957 に答える