web-dev-qa-db-ja.com

カップリング:理論と現実

カップリングは、あるオブジェクトが別のオブジェクトについて持つ知識として定義されます。これは、それらがどのように依存しているかを示します。 1つの変更は2番目に影響するため、依存度が高いほど悪くなります。高い結合は悪い、低い結合は良い。 異なるカップリングタイプ があります。

コールカップリングについて説明するとします。

Javaでは、AがオブジェクトBを作成し、そのメソッドの1つを呼び出すと、密結合と呼ばれます。しかし、BがIBを実装するAから使用されるインターフェイスIBを作成すると、疎結合であると言われます。インターフェースの1つの変更がAとBに影響を与えるため、理由はわかりません。Bの1つの変更は、IBとAに影響を与えます。これらは依然としてコールカップリングのようです。

同じ理論的根拠がFacade GoFデザインパターンに適用されます。サブシステムとクライアントコードの間に仲介者を置くため、低結合を促進すると言われています。この場合、問題をクライアントコードからFacadeに転送したようです。サブシステムの変更は、クライアントコードではなくFacadeに影響を与えるためです。クライアントコードはサブシステムに結合されなくなりましたが、ファサードは結合されました。

カップリングがどのように減少するのかわかりません。

これは次のように尋ねられました: Java実装クラスが必須であり、インターフェースコントラクトにバインドされている場合)のインターフェースを使用して疎結合はどのように達成されますか?

しかし、答えは十分に具体的ではありません。まず、カプセル化はオブジェクトをブラックボックスとして扱うことでもあるので、最初の答えは、通常のクラスと比較してインターフェイスを使用することによる利益を指定していません(=すべてがブラックボックスである場合、密結合)。したがって、答えは無効です。複数の実装が存在する場合、インターフェースが提供するのは、インターフェースと実装の間の分離です。しかし、コールカップリングに関連する問題は何も解決しません。上記のリンクで、「実装カップリング」という新しいカテゴリが追加される場合があります。これに関係なく、ソリューションはまだコールカップリングされています。 2番目の回答では、データカップリングについて説明していますが、私の知る限り、問題はコールカップリングに関するものです。

残りの回答は無関係です。

「デザインパターン-ファサードパターンを理解する」については、ファサードパターンを理解しています。私はパターンによって減少する結合についてのみ尋ねています。これは私の推論に基づいて削減されていませんが転送されます。

この主題は扱われましたが、適切な回答はありませんでした。

24
Asier Naiz

高い結合は悪い、低い結合は良い。

私はそれをそれほど白黒にしないでしょう。いくつかのカップリングが必要です。さらに、カップリングを取り除くためのいくつかの迂回的な方法は、オーバーヘッドが大きすぎる可能性があります。特に、リアルタイムで応答する必要があるアプリケーションの場合。それはトレードオフです。

はい、高結合を避けたいです。ただし、結合を低下させるためにパターンを導入することにより、最初に要件(応答時間、時間予算などが含まれる可能性があります)を満たすことが妨げられる場合は、価値がありません。


Javaでは、AがオブジェクトBを作成し、そのメソッドの1つを呼び出すと、密結合と呼ばれます。しかし、BがIBを実装するAから使用されるインターフェイスIBを作成すると、疎結合であると言われます。インターフェースの1つの変更がAとBに影響を与えるため、理由はわかりません。Bの1つの変更は、IBとAに影響を与えます。これらは依然としてコールカップリングのようです。

はい、それらはまだ結合された呼び出しです。 まあ、メトリックの定義方法によって異なります。ただし、それを処理する場合、インターフェイスは適切なツールではありません。

いずれにせよ、インターフェースを使用すると、AはBに直接結合されないため、クラスは疎結合になります。インターフェースの他の実装も可能です。

一般的なアンチパターンは、消費されたクラスが提供するものと一致するインターフェースを作成することです。コンシューマークラスが必要とするものと一致するインターフェイスを作成する必要があります。 インターフェイスの分離 を参照してください。インターフェイスは、消費されたクラスの要件になります。

このようにインターフェースを概念化すると、インターフェースは、そのコンシューマーにとって必要な場合にのみ変更されます。 Aを変更する場合、インターフェースを変更するか、新しいインターフェースを使用するかを決定できます。

Aを変更するときにインターフェースを変更することを決定した場合、変更はAからBに伝搬します。BからAに伝搬する代わりに、インターフェースを変更することを決定する必要はありません。または、実装するアダプターを導入できます。インターフェースとラップB。つまり、変更の伝播を停止する機会があります。それが私たちの望みです。そして、それが疎結合(まだ結合である)が私たちを買うものです。選択肢を増やすために設計しました。

繰り返しますが、これはコールカップリングを解決するものではありません。


同じ理論的根拠がFacade GoFデザインパターンに適用されます。サブシステムとクライアントコードの間に仲介者を置くため、低結合を促進すると言われています。この場合、問題をクライアントコードからFacadeに転送したようです。サブシステムの変更は、クライアントコードではなくFacadeに影響を与えるためです。クライアントコードはサブシステムに結合されなくなりましたが、ファサードは結合されました。

ファサードは、背後にあるものをすべて隠します。これは、オブジェクトがその状態をカプセル化する方法に似ています。ファサードは(サブ)システムをカプセル化します。コンシューマーコードはファサードと対話するだけでよく、その背後にある詳細を認識しません。

もちろん、それはまだ結合されています。そして、はい、あなたは問題をファサードに移しました。ただし、ファサードのおかげで、ファサードの背後にあるものの変更のために、コンシューマーコードを変更する必要はありません。


しかし、答えは十分に具体的ではありません。まず、カプセル化はオブジェクトをブラックボックスとして扱うことでもあるので、最初の答えは、通常のクラスと比較してインターフェイスを使用することによる利益を指定していません(=密結合、すべてがブラックボックスの場合)

クラスを直接使用する場合、コンシューマコードに必要なものはすべてクラスで実装する必要があります。コンシューマコードが代わりにインターフェイスを使用する場合、特定のクラスで実装する必要はありません。コンシューマーコードに気付かれずにクラスを変更できます。コンシューマーコードは知識が少ないため、結合度が低くなります。


複数の実装が存在する場合、インターフェースが提供するのは、インターフェースと実装の間の分離です。しかし、コールカップリングに関連する問題は何も解決しません。

正解です、これは通話のカップリングに関するものではありません。あなたは、2つのクラスとインターフェースの間の結合を呼び出すための議論を狭めるものです。そして、なぜ彼らは何も提供しないのだろうか。

インターフェイスは、コールカップリングを処理するための適切なツールではありません。代わりに、イベント駆動型アーキテクチャー、コンシューマーサブスクライバーパターンなどが必要です。そうすれば、反対側に実装さえないかもしれません。 もちろん、言語とランタイムで提供されていない場合は、一部のインフラストラクチャが必要になる場合があります。ああ、これはJavaです。そうです、インフラストラクチャが必要です。

27
Theraot

そして、Bの1つの変更は、IBおよびAに影響を与えます。

これは正しくありません。 signatureを変更してもIBとAに影響しますが、実装を変更しても影響はありません。 Aに影響を与えることなく、基になるデータストア、または操作の実行に使用されるアルゴリズムを変更したり、追加のログを追加したりできます。

この場合、問題をクライアントコードからFacadeに転送したようです。

はい、それがポイントです。さまざまな場所で同じ種類のクライアントコードを維持する代わりに、Facadeは抽象化として機能します。 Facade(高結合)の背後にあるものを処理する方法をクライアントが知っている代わりに、Facadeを処理する方法をクライアントが知っています(Facadeは実装の詳細を隠しているため、これは低くする必要があります)。ファサードはその背後にあるものに対処する方法を知っていますが、それは明確に定義された契約を持つ1つの場所です。その表面積が減り、実装をより簡単に変更できるようになります。

10
Telastyn

インターフェースやファサードを導入してもそれ自体結合が減少しないことは間違いありません。

インターフェースの紹介に使用できますいくつかの方法で結合を減らします。複数のクラスが同じインターフェースを実装する場合、呼び出し元はクラスの詳細から切り離されます。しかし、インターフェースが単一のクラスによってのみ実装されている場合、必ずしもこれが得られるわけではありません。

インターフェースがより高い抽象化レベルにある場合、またはクラス自体によって公開される実装の詳細を隠す場合、インターフェースは呼び出し元をクラスの詳細から切り離すこともできます。しかし、インターフェースを導入しても自動的にその抽象化が得られるわけではなく、この目的のためにインターフェースを注意深く設計する必要があります。

つまり、インターフェースやファサードは、カップリングを自動的に削減しません。しかし、それらは結合を減らすために使用できます。

6
JacquesB

必須カップリングは削除できません。ファイルから読み取るために.read()を呼び出す必要があり、終了時に.close()を呼び出す必要がある場合は、そのようにします。

ただし、クラスFileへの他の変更を検討してください。実装に変更があった場合は、Fileを使用するコードを再コンパイルする必要があります。これは、コンパイラがFileインスタンスが言及されているすべての場所で、そのすべてのメソッドが呼び出されることを確認する必要があるためです。正しく—コードが.read().close()以外を呼び出さなくても。 Fileが広く使用されている場合、これは面倒になります。

ここで、2つのメソッド.read()および.close()のみを公開したインターフェースReadableについて考えてみます。

Fileクラスはこのメソッドを実装します。パブリックメソッドでさえ、その実装に関する変更があったとしても、それは問題ではありません。それでもReadableを実装し、コンパイラはそれをチェックします。

これで、コードがReadableではなくFileに依存するようになります。 Fileが公開するメソッドを使用しないことが保証されていますが、必要はありません。 Fileの実装への変更は、コードにとっても重要ではありません。再コンパイルする必要はありません。コードでFileの新しい実装を使用して.jar/.dll/.soをスローするだけで動作します。

ほら、カップリングを減らしたところです。

4
9000

現在の回答はあなたが探している情報を提供していないようですので、このトピックに対してさらに別のアプローチを試みます。

次の疑似コードを考えてみます(私はJava devではありません)。

class Logging
{
  public Logging(IWritableDatastore writable_ds)
  {
    ds = writable_ds;
    dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
  }

  public void write_entry(string message)
  {
    ds.write(dtf.format(LocalDateTime.now()) + message);
  }

  private IWritableDatastore ds;
  private DateTimeFormatter dtf;
}

interface IWriteableDatastore
{
  void write(string data);
}

class FileWriter implements IWriteableDatastore
{
  public void write(string data)
  {
    // Do whatever Java stuff needed to write to file...
  }
}

FileWriterクラスは、Loggingクラスと疎結合されています。依存関係の注入を使用しているため、ロギングはFileWriterが存在することさえ知りません。これにより、FileWriterを変更する自由が与えられます。また、ログをテキストファイルではなくデータベースに書き込む場合に備えて、さまざまなデータストアを自由に追加できます。

多分この例はあなたに理にかなっています。

0
sbecker

Javaでは、AがオブジェクトBを作成し、そのメソッドの1つを呼び出すと、密結合と呼ばれます。

誰が言ったのですか?それは確かに以下の状況よりより緊密に結合されていますですが、緊密さと緩みは相対的な用語であり、絶対的なものではありません。

しかし、BがIBを実装するAから使用されるインターフェイスIBを作成すると、疎結合であると言われます。

より緩く(またはより緩く)結合。

インターフェースの1つの変更がAとBに影響を与えるため、理由はわかりません。

あなたは他の変更を完全に無視しています:あなたはビットを削除しました

... AはオブジェクトBを作成します...

したがって、Aはおよび呼び出し Bを作成しないため、2番目の形式は密結合性が低くなり、呼び出しのみを行います。作成もカップリングです(これには、呼び出し、および場合によってはコンストラクターパラメーターも含まれます)。

just AからBの構築を削除し、直接呼び出しを維持すると、still結合が減少します。これで、Bのコンストラクターへの変更をAから隠すことができます。これは、ファクトリーの内部または外部からBを渡すことによって、気にする必要がないはずです。

ファサードとポリモーフィズム

次に、一般的なインターフェース、特にファサードについて説明します。リンクされた質問に対する回答の読み方が間違っています。焦点のレンズを通して特定の1つのケースを読んでいるためです正確に2つのモジュール間の結合を呼び出す

受け入れられた答えは、Facadeが同じインターフェースの複数の実装を処理することであることは非常に明確です。 thatの場合、インターフェースを使用せずに、呼び出しモジュールは可能な各実装に結合されます。

BをIBで置き換える場合、Aは複数のIB実装を知る必要なしに(つまり、具象サブクラスに結合せずに)動作できるようになるため、結合を減らします。

お気づきのように、これはcallカップリングを削減しませんが、AはIBのみにコールカップリングされ、-ifは複数のサブクラスを追加しますthen Aのコールカップリングは、インターフェイスがない場合ほど増加しません。

インターフェースとモノモーフィズム

IBの実装が実際に1つしかない縮退した場合はどうでしょうか。

ここで、観察したように、コールカップリングは減少していません。ただし、コールカップリングへの焦点は還元的です。AがBに依存する場合、ある意味ではBのすべての依存関係と実装の詳細にも依存します。 Aを分離してテストするには、Bを模擬するために追加の作業を行う必要があります。これらは、少なくとも時々-避ける価値のある結合の形式でもあります。

0
Useless