web-dev-qa-db-ja.com

全二重通信オブジェクトは単一の責任と競合しますか?

私はJavaでWebsocket実装を作成しており、基本的にInputStreamとOutputStreamをラップするだけで、送信と受信の両方にパブリックメソッドを持つように設定しました。入力と出力は関連していますが、それらも完全に反対であり、機能をほとんど共有していません。代わりにWebsocketInputクラスとWebsocketOutputクラスを使用する方が理にかなっていますか?

JavaのSocketクラスには、InputStreamとOutputStreamを提供する以外に、実際には送受信するためのメソッドがありません。 Javaで、読み取りと書き込みの両方をカプセル化する一般的に使用されるクラスを認識していませんが、ここでそれらを分割するのは間違っていると感じています。ここでの最善のアプローチは何でしょうか?

3
codebreaker

ここでの他の非常に良い答えに加えて、それを考える1つの方法は、入出力ストリームに個別のラッパーを適用することによって、(全二重機能の方向で)抽象化のレベルを実際に改善していないということです。

確かに、入力ストリームと出力ストリームは、抽象化レベルで見ると、個別に単一の責任を果たします。一方、入力と出力のバンドルは、より高いレベルの抽象化、つまり全二重ストリームを形成します。より高いレベルの抽象化を持っている、それをそのように見ると、それはそれ自身の単一の責任、つまり全二重comリンクを形成するための適切な入力と出力ストリームのペアリングを持っています。

これで、全二重を提供する単一のエンティティができ、他のエンティティに渡すことができるため、2つを組み合わせて抽象化のレベルを上げることを示唆するヒントがあります。

入力と出力を個別に別々にラップする場合、(まだ)2つの抽象化を渡す必要があり、クライアントは、少なくとも渡されたときにそれらを一緒に維持しない場合でも、2つのペアリング(潜在的には詳細)に注意を払う必要があります、全二重抽象化を形成します。 (ラップされた入力と出力を組み合わせて、最終的に優れた全二重抽象化を明示することもできますが、中間ラッパーのポイントは何ですか?)

したがって、基礎となる単方向ストリームにはそれぞれ独自の単一責任がありますが、それらをバンドルするクラスにも独自の単一責任があります。これらは相互に排他的ではありません。後者はより高いレベルの抽象化であるためです。これらのクラスをまとめると、階層化が示されます。これは、設計を単一責任に集中させる1つの方法です。階層化とは、1つの抽象化が下の次の層の抽象化によって完全に実装され、下位に移動する必要がない場合です。 (階層化は、非常に大規模なソフトウェアプロジェクトの複雑さを軽減するために私が好む手法です。)

3
Erik Eidt

それをいくつかのクラスに分割したい場合、Javaアプローチは、まさにそれをアプローチする方法です。

  • クラスSocket
    • メソッドgetInputStreamInputStreamを返します
    • メソッドgetOutputStreamOutputStreamを返します
    • Socket自体に適用され、入力アスペクトまたは出力アスペクトのみで個別に制御することは許可されていない他のメソッド。

要するに:

  • 基礎となるメカニズムはソケットです(技術的に言えば)。
    • (他の回答者が指摘するように)このように機能しないため、ユーザーからこの全二重メカニズムを隠そうとすることは不可能です。
    • つまり、このメカニズムクラスを2つの独立したクラスに分割しない方がよいでしょう。
  • ただし、それぞれが単一の側面(入力または出力)を公開するファサードまたはインターフェイスを提供することは許可されています。
    • これは、インターフェース分離の原則を満たします。

これはまさにJavaが行うことです。


選択肢のリストは次のとおりです。

  • 1つのメインクラスと、メインクラスからアクセスできる2つのファサードクラス(上記で説明)
  • 1つのクラス、2つ以上のインターフェース。各インターフェイスでメソッドを呼び出すために、ユーザーはインターフェイスキャストを実行します。
  • 1つのメインクラスのみ。両方の側面(入力ストリームと出力ストリーム)に対応するすべてのメソッドは、メインクラスに積み上げられます。

後者の2つのオプションでは、メソッド名の競合は許可されていません。言い換えれば、ユーザーにとって難しいことは別として、メソッドに名前を付けることも難しいということです。

3番目のオプションは、最も柔軟性がありません。そのメインクラスを使用するように記述されたコードは、たとえば、2つの半二重通信をペアにして形成される全二重通信に適合させることはできません。


一方、通信プロトコルの設計全体がすでにある場合もあります。この場合、(接続の管理に必要なものを除いて)下位レベルの詳細を公開する必要はありません。代わりに、通信プロトコルの機能を公開する必要があります。つまり、必要がなければ汎用クラスを作成する必要はありません。

たとえば、バイトストリームレベルで動作する代わりに、構造化され、検証されたメッセージを送受信するクラスが必要になる場合があります。そのようなクラスがデザインに存在する場合、そのようなクラスはSocketクラスを直接使用するように記述できるため、抽象化のレイヤーをスキップします。

4
rwong

全二重通信回線は単一責任です。それに応じてそれを扱います。

2
Robert Harvey

入力ストリームと出力ストリームを直接使用するのではなく、ラップする理由が必要です。あなたはペアで両方を必要とするクライアントを持っているかもしれません。たぶん、そのペアの詳細は一緒に変わります。

この理由が2つの理由ではなく、1つの理由である場合、クラスが変更される理由は1つだけです。あなたがそれをあなたの罰金と言うことができるならば。

この問題は、クラスで扱われるコミュニケーションの形態の数に関するものではありません。それはあなたがこのクラスを選ぶ理由の数についてです。

このクラスは、それを介して送信されたすべてのものをログに記録するラッパーである可能性があります。すべてをdev/nullに送信するものである可能性があります。それはそれが行くことになっている場所にすべてを送るものかもしれません。これらの3つの理由を満たすと、3つのクラスが生成されます。 6ではありません。9ではありません。

つまり、全二重通信は単一の責任です。それがクラスが提供するものである場合、あなたは単一責任の原則に従っています。それ以上ではありません。それ以下ではありません。

1
candied_orange