私はその場で誰か他の人のコードを書き直しているところですが、お互いを直接参照し、お互いにメソッドを呼び出す2つのクラスに出くわしました。そのように(C#の例):
class A {
B otherClass;
public A() {
this.otherClass = new B(this);
}
// Other methods here call methods on B
}
class B {
A otherClass;
public B(A otherClass) {
this.otherClass = otherClass;
}
// Other methods here call methods on A
}
彼のコードを読んで理解しようとすると、2つのクラスが相互に呼び出しながら移動する必要があります。
これらのクラスは、他を使用せずに独立して再利用することはできません。循環参照を回避することがベストプラクティスと見なされていることは承知しています(Visual StudioのC#プロジェクト間では完全に許可されていません)。
通常は、2つのクラスが論理モジュールとして独立しているのではなく、それらのlifetimesが独立しているということです。優れた例は、AがLinkedListで、Bがノードの場合です。概念的には、それらはLinkedListの1つにすぎませんが、Bを個別に作成および破棄できるようにするには、Bを個別にする必要があります。
それが当てはまらない場合は、1つの大きなクラスを作成することを検討してください。
人々は、正当な理由なしにクラスを分割することはめったにありません。彼らが問題を抱えているのは、その方法の詳細です。クラスを再結合する前に、クラス間でメソッドを再割り当てして、3番目のクラスの導入を検討してください。
メソッドが別のクラスを呼び出す場合は、メソッドをその別のクラスに移動してみてください。これは、プッシュをプルに変換するようなものです。これは同じタスクを逆に実行します。この演習では、結合を大幅に減らすことができます。
しばしば役立つもう1つのことは、3番目のクラスの導入です。これは、データを共有するPODオブジェクト、またはデータベースカーソルに似たある種の結果オブジェクトなど、両方のオブジェクトのインスタンスを含み、それらの間で調停する下のレイヤーにある、上のレイヤーにある場合があります。このアプローチは、2つの密結合クラスの代わりに3つあるように感じるため、多くの人が無視しますが、このパターンは、依存関係が一方向にのみ流れるようにするのに役立ちます。
これはおそらく最良のセットアップではありませんが、1つの大きなクラスよりも、密結合されたクラスのペアの方がいくつかの利点があります。
JavaでGoogle Web Toolkitを操作しているときに、MVPパターンの一部としてこのパターンに出くわしました。 Class A
はビューで、Class B
はプレゼンターです。プレゼンターにはすべてのロジックが含まれていますが、ビューはブラウザーDOMに依存しているため、テストが困難です。モックバージョンのビューをプレゼンターに挿入することで、アプリケーションをよりテストしやすくなります。
モックによるテストのしやすさのためにクラスが分割されていない場合、この分割はおそらく正しい方向への一歩ではあるものの、完全には役に立たないと主張します。確かに、コードをその機能で分離すると役立つ場合があります(たとえば、A
がクエリを管理しているときにクラスB
がデータベース接続を管理していた場合など)、循環コードが含まれないようにコードを再設計できます依存。
AとBは明確に分離した懸念事項を持っていますか?答えが「はい」の場合、それらが2つの別々のクラスとして保持されている場合に適しています。また、循環参照を回避する場合、たとえばAとBを個別にテストする単体テストを作成する場合は、A、B、またはその両方のインターフェイスを定義し、インターフェイスを使用することによってのみ、BでAのメソッドを直接呼び出さないでください。
クラスについて説明したとおり、問題ないと思います。実際には循環参照ではありません。 Aはインスタンスに依存していますB&BはAのインスタンスなしでは存在できません。AでBのインスタンスを作成することはできません。
私が注目するのは、各クラス内の機能が適切かどうかだけです。
つまり、それぞれをブラックボックスと考えます。 Bから新しい派生クラスを作成してAで使用できますか? Aが何をしていたか知らずにそれを行うことができますか? Bが何をしているのかわからずにAでも同じことをしていただけませんか
C#には多重継承がないため、クラスAとBはどちらも他のクラスから派生できますが、単一のクラスでは不可能です。
時々、この種の相互依存はかなり自然に見えます。車とドライバーの関係について考えてください。両方を1つのクラスにまとめるのは自然な感じではありません。
ただし、両方のクラスを互いに直接依存させるのではなく、インターフェースを介してお互いに通知する方がよいでしょう。これにより、モジュール性が向上し、1つのクラスに別の実装を提供し、他のクラスには手を加えないようにすることができます。
2つのクラスが相互に行き来している場合、抽象化を正しく実行できなかった可能性があります。そのコードを1つのクラスに組み合わせると、SOLIDの原則を念頭に置いている場合、単一の責任が(まだ壊れていなければ)損なわれます。レガシーコードをリファクタリングして自分のやり方で作業しようとすると、ユニットテストは、各クラスが達成しようとしていることを明らかにするのに役立ちます。