2013-03-11 7 views
6

私は最近、TDDとクリーンなコードについて多くのことを読んできましたので、これらを使用する簡単なプロジェクトに取り掛かり、取り組むべき最良のアプローチが本当にわからないことに反対してきました。呼び出し元はコンストラクタを呼び出す前に引数の有効性をチェックする必要がありますか?

Java Fileオブジェクトをパラメータとするクラスがありますが、この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"); 
    } 
    .... 
} 

私は多分オブジェクトを作成するファクトリメソッドを追加し、Fileが無効の場合はnullを返す考えました。

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の原則(単一責任、カップリングなど)の面でこれを行うのが好ましい方法ですか?

UPDATE:

は、私は私の現在の状況に適用できるのではなく、一般的に私の質問は、約本当にあったようになり、別の可能性について考え始め、すでに掲載されている答えのいくつかを読みました。

私は呼び出し元のコードの一部としてファイルシステムからのパスを持っており、そのディレクトリ内のすべてのファイルをリストしており、有効かどうかにかかわらずMyObjectコンストラクタに渡すファイルはそれぞれです。 FileFilterをメソッドlistFilesに渡すと、listFilesが有効なディレクトリのみを返すことが保証されます。 FileFilterはMyObjectに内で宣言することができます

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

私のコンストラクタが例外をスローした場合に期待が、それが唯一の有効なディレクトリを渡されているということですので、それは本当に例外的な状況になります。これを行うと、コンストラクタ/ファクトリからチェック例外の必要性を取り除くことができます。これは、例外が予想される動作ではなくどこかのバグを示すためです。しかし、それはコンストラクタかファクトリメソッドのどちらに入れるのかという疑問が残っています。

+0

私にとっては、3番目が好ましいでしょう。短いとあなたは、いくつかのファイルでそれを使用することができます。また、isValidメソッドから新しいオブジェクトを返す必要はなく、isValidがコードの別の部分からtrueを返す場合にのみオブジェクトを作成します。これは、機能を分離するので、良いことです。 –

+0

@AliAlamiriクリーンなコードは、私の好みです。なぜなら、読むことが最も理にかなっています。つまり、何が起こっているのかが読者にはっきりと分かります。私は、発信者が最初にisValidを呼び出すべきであることを認識しなければならないという事実が好きかどうかを判断できません。 – DaveJohnston

+0

ファイルをコンストラクタに渡し、ファイルisValidが存在するかどうか確認してください。これは、オブジェクトを構築するときに呼び出し元からのチェックを隠し、渡されるファイルをコンストラクタでチェックするようにしますか? –

答えて

1

与えられた引数がstatic boolean isValid()検証メソッドで補完された契約を完全に満たしていない場合、検証を行い、例外をスローするコンストラクタの組み合わせを提供することが私の好みです。

検証メソッドは、有効なオブジェクトを構築するうえで、わかりやすく読みやすい方法を提供しますが、コンストラクタは、検証されていない使用が必要なときに例外をスローすることによって処理されるようにします。

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を返すことはあなたには、いくつかの前提条件が満たされなかったユーザーに警告するために例外を投げることができる不正なオプションであるため。

UPDATE: この戦略が有効な状態で唯一のオブジェクトが作成される保証を提供します。 また、MyObjectのインスタンスをモックアウトしたい場合は、実行時ポリモーフィズムを使用するためのインタフェースを実装し、モックオブジェクトを渡すことができます。希望は意味をなさない。

+0

TDDの別のノートでは、これは私がテスト可能なコードを書くための基礎を理解するのに良いリソースですhttp://misko.hevery.com/code-reviewers-guide/ –

1

私は、検証コードがどこにあるかは、 'MyObject'クラスが表すものによって決まると思います。

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

MyObjectがファイル/ディレクトリのコンテナであり、ディレクトリ固有のコードがない場合は、にはそのクラスに検証コードを入れます。にはファイルではなくディレクトリが必要です。

+0

検証コードをコンストラクタに保持することはお勧めできませんテスト中にこの動作に固執していて、ここではモックを渡すことはできません。いくつかのモック値を持つモックを作成しようとすると、それらのモック値は、インスタンス化が成功するために有効でなければなりません。 –

+0

コンストラクタ内の引数を調べることは、かなり標準的な方法であると思います.Joshua Blochは、効果的なJavaを強く推奨しています。「コンストラクタは、後で使用するために保存するパラメータの妥当性をチェックするという原則の特殊なケースです。クラス不変式に違反するオブジェクトの構築を防ぐために、コンストラクタパラメータの妥当性をチェックすることが重要です。 – codebox

+0

コンストラクターには代入のみを含める必要があり、事前条件検証は、そのファクトリメソッドまたはファクトリのいずれかで行う必要があります。 http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/これはこの方法論を促進する良いリソースです。 –

0

それが依存...コーディングの観点から

、もっともシンプルでクリーンなアプローチが爆発(unchdcked)コンストラクタです。呼び出し元がディレクトリを渡すしかなく、ディレクトリを渡す必要があるという合理的な期待がある場合は、そのディレクトリに移動します。

呼び出し側が非ディレクトリに渡すことが合理的な期待がある場合は、次の2つの選択肢があります:呼び出し側は状況に対処することができた場合は、

  1. をコンストラクタが確認例外をスローしてい
  2. 呼び出し元が例外を処理できない場合、呼び出し元がメソッドを呼び出す場合、クラスは「何もしない」ことができます。コンストラクタに渡されたファイルがディレクトリでない場合は、
+0

考えられるのは、ファイルシステム内のパスを指定すると、コードはその内容を反復して、与えられた接頭辞を持つすべてのディレクトリのリストを作成するということです。したがって、内容の一部が有効でないことを期待するのは完全に合理的です。したがって、これらのファイル/ディレクトリをリストに追加することは望ましくありません。私はコンストラクタに例外を追加するかどうか、またはFactoryと一緒に行くのが良いかどうかについてはわかりませんでした。 – DaveJohnston

+0

工場であるかどうかにかかわらず、誰が問題を処理するかを決定する必要があります。一度それを理解すれば、アプローチは明白でなければなりません。この場合、ファクトリは助けにならないことに注意してください。同じ選択肢が残っていて、別のメソッドに移動したばかりです。ファクトリは、コンストラクタの中からメソッドに 'this'が渡されたときにこの「エスケープ」問題が発生するのを避けるために、オブジェクトの作成後に何かを行う必要がある場合にのみ便利です。 – Bohemian

関連する問題