システムが複雑だからといって、それを複雑にしなければならないという意味ではありません。このような依存関係(または共同作業者)が多すぎるクラスがある場合:
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クラスから削除できます。CD4
この例は非常に抽象的であり、多くの仮定を立てているので、石のように私の答えを受け取らないでください。これをリファクタリングする方法は他にもあると確信していますが、少なくとも手順を実行することで、クラスを分割する代わりに何らかのプロセスを実行して責任を移動させることができます。
編集:
@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を使用しない(決して使用しない)(または少なくとも頻繁に使用する)」で詳しく説明しています。コンストラクターまたは静的メソッドを使用してサブオブジェクトを作成しやすくする代わりに、流体ビルダーパターンを使用できます。