私は何度もAPIの設計/実装に携わっており、このジレンマに直面しています。
私は情報隠蔽を非常に強力に支持しており、内部クラス、プライベートメソッド、パッケージプライベート修飾子などを含むがこれらに限定されないさまざまな手法を使用しようとしています。
これらの手法の問題は、優れたテスト容易性を妨げる傾向があることです。そして、これらのテクニックのいくつかは解決できますが(たとえば、クラスを同じパッケージに入れることによるパッケージのプライベート性)、他のテクニックはそれほど簡単に取り組むことができず、反射魔法または他のトリックが必要です。
具体的な例を見てみましょう。
public class Foo {
SomeType attr1;
SomeType attr2;
SomeType attr3;
public void someMethod() {
// calculate x, y and z
SomethingThatExpectsMyInterface something = ...;
something.submit(new InnerFoo(x, y, z));
}
private class InnerFoo implements MyInterface {
private final SomeType arg1;
private final SomeType arg2;
private final SomeType arg3;
InnerFoo(SomeType arg1, SomeType arg2, SomeType arg3) {
this.arg1 = arg1;
this.arg2 = arg2;
this.arg3 = arg3;
}
@Override
private void methodOfMyInterface() {
//has access to attr1, attr2, attr3, arg1, arg2, arg3
}
}
}
公開しないことには大きな理由がありますInnerFoo
。他のクラスはありません。ライブラリは公開契約を定義しておらず、作成者が意図的にアクセスできるようにしたくないため、ライブラリにアクセスする必要があります。ただし、100%TDDコーシャであり、リフレクショントリックなしでアクセスできるようにするには、InnerFoo
次のようにリファクタリングする必要があります。
private class OuterFoo implements MyInterface {
private final SomeType arg1;
private final SomeType arg2;
private final SomeType arg3;
private final SomeType attr1;
private final SomeType attr2;
private final SomeType attr3;
OuterFoo(SomeType arg1, SomeType arg2, SomeType arg3, SomeType attr1, SomeType attr2, SomeType attr3) {
this.arg1 = arg1;
this.arg2 = arg2;
this.arg3 = arg3;
this.attr1 = attr1;
this.attr2 = attr2;
this.attr3 = attr3;
}
@Override
private void methodOfMyInterface() {
//can be unit tested without reflection magic
}
}
この例には3つの属性しか含まれていませんが、5〜6を使用するのはかなり合理的であり、OuterFoo
コンストラクターは8〜10のパラメーターを受け入れる必要があります。ゲッターを上に追加すると、すでに100行の完全に役に立たないコードがあります(テスト用にこれらの属性を取得するには、ゲッターも必要になります)。はい、ビルダーパターンを提供することで状況を少し改善することができますが、これは過剰なエンジニアリングであるだけでなく、TDD自体の目的を損なうものだと思います。
この問題の別の解決策は、クラスの保護されたメソッドを公開し、Foo
それを拡張してFooTest
、必要なデータを取得することです。protected
繰り返しになりますが、メソッドはコントラクトを定義し、それを公開することで暗黙的に署名したため、これも悪いアプローチだと思います。
誤解しないでください。テスト可能なコードを書くのが好きです。簡潔でクリーンなAPI、短いコードブロック、読みやすさなどが好きです。しかし、単体テストが簡単であるという理由だけで情報隠蔽に関して犠牲を払うのは好きではありません。
誰かがこれについて(一般的に、そして特に)何か考えを提供できますか?与えられた例のための他のより良い解決策はありますか?