web-dev-qa-db-ja.com

大規模な密結合クラスを分割する方法は?

可能な場合はリファクタリングして、より軽量でクリーンなデザインにするために、2k行を超えるコード(および成長)の巨大なクラスがいくつかあります。

それが非常に大きい理由は、これらのクラスがほとんどのメソッドがアクセスする必要のあるマップのセットを処理し、メソッドが互いに非常に接続されているためです。

非常に具体的な例を示します。着信メッセージを処理するServerというクラスがあります。 joinChatroomsearchUserssendPrivateMessageなどのメソッドがあります。これらのメソッドはすべて、userschatroomsservers、...

チャットルームに関するメッセージを処理するクラス、ユーザーに関するすべてを処理するクラスなどがあればいいかもしれませんが、ここでの主な問題は、ほとんどのメソッドですべてのマップを使用する必要があることです。これらがすべて共通のマップに依存しており、メソッドが互いに非常に接続されているため、現時点ではすべてServerクラスに固定されています。

クラスChatroomsを作成する必要がありますが、他の各オブジェクトへの参照が必要です。他のすべてのオブジェクトなどへの参照を持つクラスユーザー.

私は何か間違ったことをしているように感じます。

14
Matthew

あなたの説明から、私はあなたのマップが純粋にデータのバッグであり、Serverメソッドのすべてのロジックを持っていると思います。すべてのチャットルームロジックを別のクラスにプッシュすることで、データを含むマップに行き詰まります。

代わりに、個々のチャットルーム、ユーザーなどをオブジェクトとしてモデル化してください。そうすることで、データの巨大なマップではなく、特定のメソッドに必要な特定のオブジェクトのみを渡します。

例えば:

public class User {
  private String name;
  ...

  public void sendMessage(String message) {
    ...
  }
}

public class Chatroom {
  // users in this chatroom
  private Collection<User> users;

  public void add(User user) {
    users.add(user);
  }

  public void sendMessage(String msg) {
    for (User user : users)
      user.sendMessage(msg);
  }
}

public class Server {
  // all users on the server
  private Collection<User> users;

  // all chatrooms on the server
  private Collection<Chatroom> chatrooms;

  /* methods to handle incoming messages */
}

これで、メッセージを処理するための特定のメソッドを簡単に呼び出すことができます。

チャットルームに参加したいですか?

chatroom.add(user);

プライベートメッセージを送りたいですか?

user.sendMessage(msg);

公開メッセージを送信したいですか?

chatroom.sendMessage(msg);
10
casablanca

各コレクションを保持するクラスを作成できるはずです。 Serverはこれらの各コレクションへの参照を必要としますが、基になるコレクションへのアクセスや維持を必要としない最小限のロジックのみが必要です。これにより、サーバーの動作がより明確になり、サーバーの動作が分離されます。

5
Peter Lawrey

私がこのような大きなクラスを見たとき、そこに出ようとするクラス(またはそれ以上)がしばしばあることがわかりました。このクラスに関連していないと思われるメソッドを知っている場合は、静的にしてください。コンパイラーは、このmethdが呼び出す他のメソッドを通知します。 Javaは、それらも静的であると主張します。それらを静的にします。コンパイラーは、呼び出すすべてのメソッドを通知します。コンパイルエラーがなくなるまで、これを何度も繰り返します。 。次に、大きなクラスに静的メソッドをロードします。これらを新しいクラスに引き出して、メソッドを非静的にすることができます。次に、この新しいクラスを元の大きなクラスから呼び出すことができます(行の数が少なくなっているはずです)。 )

その後、クラスのデザインに満足するまでこのプロセスを繰り返すことができます。

マーティン・ファウラーの本は本当に良い本ですので、この静的なトリックを使用できない場合があるので、私もこれをお勧めします。

4
RNJ

追加する casablancaの洞察に満ちた答え -複数のクラスが何らかのタイプのエンティティで同じ基本的なことを行う必要がある場合(コレクションへのユーザーの追加、メッセージの処理など)、それらのプロセスも個別に保持する必要があります。

これを行うには、継承または構成による複数の方法があります。継承については、たとえば、ユーザーやメッセージを処理するフィールドと機能を提供し、chatroomuser(またはその他)などのエンティティがそれらを拡張する具体的なメソッドを持つ抽象基本クラスを使用できます。クラス。

さまざまな理由で 、継承よりも構成を使用することは、一般的なルールとして適切です。コンポジションを使用して、これをさまざまな方法で実行できます。ユーザーまたはメッセージの処理はクラスの責任の中心的な機能であるため、コンストラクターの注入が最も適切であると主張できます。このように、依存関係は透過的であり、必要な機能がないとオブジェクトを作成できません。ユーザーまたはメッセージの処理方法が変更または拡張される可能性がある場合は、 戦略パターン のようなものの使用を検討する必要があります。

どちらの場合も、柔軟性を実現する具体的なクラスではなく、インターフェイスに向けてコーディングするようにしてください。

そうは言っても、そのようなパターンを使用するときは、複雑さが増すことによるコストを常に考慮してください。必要がない場合は、コーディングしないでください。ユーザー/メッセージの処理方法を変更するつもりがないことがわかっている場合は、戦略パターンの構造的な複雑さは必要ありませんが、懸念事項を分離して繰り返しを避けるために、共通の機能を離婚する必要がありますそれを使用する具体的なインスタンスから-そして、逆のオーバーライドする理由が存在しない場合は、そのような処理機能のユーザー(チャットルーム、ユーザー)を、処理を行うオブジェクトで構成します。

したがって、要約すると:

  1. Casablancaが書いたように:チャットルーム、ユーザーなどを分離してカプセル化します。
  2. 個別の共通機能
  3. 個々の機能を分離して、データ表現(およびアクセスとミューテーション)を、データまたはその集計の個々のインスタンス(たとえば、searchUsersのようなものをコレクションクラスに入れることができる)のより複雑な機能から分離することを検討してください。リポジトリ/アイデンティティマップ)
1
Michael Bauer

ほとんどのコードが存在するため、ヘルパークラスを使用してメソッドを移動することをお勧めします。それは簡単なリファクタリングに役立ちます。したがって、サーバークラスにはマップが含まれています。ただし、ヘルパークラスを使用して、join(Map chatrooms、String user)、List getUsers(Map chatrooms)、Map getChatrooms(String user)などのメソッドを持つChatroomHelperを使用します。

サーバークラスはChatroomHelper、UserHelperなどのインスタンスを保持するため、メソッドを論理ヘルパークラスに移動します。これにより、パブリックメソッドをサーバーにそのまま残すことができるため、呼び出し元を変更する必要はありません。

1
techuser soma

私は 同じ答えを使用します 他の場所で提供した:モノリシッククラスを取り、その責任を他のクラスに分割します。 DCIとビジターパターンはどちらも、そうするための優れたオプションを提供します。

0
Mario T. Lanza

ほら、私たちには問題の完全な説明がないので、あなたの質問は一般的すぎて答えられないと思います。例として、より良い設計の無益性についての懸念の1つを取り上げます。

サーバークラスと今後のチャットルームクラスがユーザーに関するデータを共有するとしますが、このデータは異なるはずです。サーバーにはおそらくユーザーのセットが接続されていますが、それ自体がサーバーに属しているチャットルームには、特定のチャットルームに現在ログインしているユーザーのみの、異なるユーザーのセット(最初のセットのサブセット)があります。

データ型が同じであっても、これは同じ情報ではありません。
優れた設計には多くの利点があります。

前述のFowlerの本はまだ読んでいませんが、Folwerが他の本を読んだことがあり、信頼できる人から勧められたので、他の人と同意するのに十分な気持ちになれます。

0
Shrulik

マップにアクセスする必要性は、メガクラスを正当化しません。ロジックをいくつかのクラスに分離する必要があり、他のクラスがマップにアクセスできるように、各クラスにはgetMapメソッドが必要です。

0