システムが複雑だからといって、それを複雑にしなければならないという意味ではありません。このような依存関係(または共同作業者)が多すぎるクラスがある場合:
public class MyAwesomeClass {
public class MyAwesomeClass(IDependency1 _d1, IDependency2 _d2, ... , IDependency20 _d20) {
// Assign it all
}
}
...それでは複雑になりすぎて、あなたは本当にSRPをフォローしていないのですか?CRCカードで何MyAwesomeClass
がインデックスカードに収まらないのかを書き留めるか、本当に小さく判読できない文字で書かなければならないと思います。
あなたがここで持っているのは、あなたの仲間が代わりにインターフェイス分離の原則に従っているだけであり、極端にそれを取ったかもしれないということですが、それはまったく別の話です。依存関係はドメインオブジェクトであると主張することができます(これは起こります)が、同時に20個のドメインオブジェクトを処理するクラスがあると、それが少し広がりすぎます。
TDDは、クラスがどれだけの能力があるかを示す良い指標となります。率直に言って; テストメソッドに、テストをリファクタリングしたとしても、書き込みに永遠にかかるセットアップコードがある場合、MyAwesomeClass
おそらく実行することが多すぎます。
では、この難問をどのように解決しますか?責任を他のクラスに移動します。この問題が発生したクラスに対して実行できる手順がいくつかあります。
- クラスが依存関係で行うすべてのアクション(または責任)を特定します。
- 密接に関連する依存関係に従ってアクションをグループ化します。
- 再委任!つまり、識別された各アクションを、新しいクラスまたは(より重要なことですが)他のクラスにリファクタリングします。
リファクタリングの責任に関する抽象的な例
してみましょうC
いくつかの依存関係を持っているクラスでD1
、D2
、D3
、D4
あなたはあまり使用するリファクタリングする必要があります。C
依存関係を呼び出すメソッドを特定したら、簡単なリストを作成できます。
D1
- performA(D2)
、performB()
D2
- performD(D1)
D3
- performE()
D4
- performF(D3)
リストを見て、私たちはそれを見ることができますD1
し、D2
クラスは何とか一緒にそれらを必要と互いに関連しています。また、そのD4
ニーズを見ることができますD3
。したがって、2つのグループがあります。
Group 1
- D1
<->D2
Group 2
-- D4
>D3
グループ化は、クラスに2つの責任があることを示す指標です。
Group 1
-互いに必要な2つのオブジェクトの呼び出しを処理するための1つ。クラスでC
両方の依存関係を処理する必要をなくし、どちらか一方を代わりにこれらの呼び出しを処理するようにすることができます。このグループ化でD1
は、への参照を持つ可能性があることは明らかですD2
。
Group 2
-他の責任は、別のオブジェクトを呼び出すために1つのオブジェクトを必要とします。クラスの代わりにD4
処理できませんD3
か?そうすれば、代わりに呼び出しを実行させることで、おそらくD3
クラスから削除できます。C
D4
この例は非常に抽象的であり、多くの仮定を立てているので、石のように私の答えを受け取らないでください。これをリファクタリングする方法は他にもあると確信していますが、少なくとも手順を実行することで、クラスを分割する代わりに何らかのプロセスを実行して責任を移動させることができます。
編集:
@Emmad Karem氏のコメント:
「クラスのコンストラクタに20個のパラメーターがある場合、チームはSRPをよく知っているようには聞こえません。1つのことだけを行うクラスがある場合、20個の依存関係があるのはどうですか?」 Customerクラスがあり、コンストラクターに20個のパラメーターがあるのは奇妙なことではありません。
確かに、DAOオブジェクトには多くのパラメーターがあり、コンストラクターで設定する必要があり、パラメーターは通常、文字列などの単純なタイプです。ただし、Customer
クラスの例では、他のクラス内でそのプロパティをグループ化して、物事を簡単にすることができます。Address
通りのあるZipcode
クラスと、郵便番号を含むクラスを持ち、データ検証などのビジネスロジックも処理するなど:
public class Address {
private String street1;
//...
private Zipcode zipcode;
// easy to extend
public bool isValid() {
return zipcode.isValid();
}
}
public class Zipcode {
private string zipcode;
public bool isValid() {
// return regex match that zipcode contains numbers
}
}
このことについては、ブログ投稿「JavaでStringを使用しない(決して使用しない)(または少なくとも頻繁に使用する)」で詳しく説明しています。コンストラクターまたは静的メソッドを使用してサブオブジェクトを作成しやすくする代わりに、流体ビルダーパターンを使用できます。