web-dev-qa-db-ja.com

「疎結合」とは何ですか?例を提供してください

「疎結合」という概念を理解できないようです。 「ゆるい」という言葉が通常否定的な意味合いを持つことは役に立たないと思うので、疎結合がgoodであることを常に忘れています。

誰かがこの概念を説明する「前」と「後」のコード(または擬似コード)を見せてくれますか?

166
trebormf

CartContentsクラスを使用してショッピングカート内の商品を追跡する簡単なショッピングカートアプリケーションと、購入を処理するためのOrderクラスを考えてみましょう。 Orderは、カート内のコンテンツの合計値を決定する必要があります。次のようにします。

密結合の例:

public class CartEntry
{
    public float Price;
    public int Quantity;
}

public class CartContents
{
    public CartEntry[] items;
}

public class Order
{
    private CartContents cart;
    private float salesTax;

    public Order(CartContents cart, float salesTax)
    {
        this.cart = cart;
        this.salesTax = salesTax;
    }

    public float OrderTotal()
    {
        float cartTotal = 0;
        for (int i = 0; i < cart.items.Length; i++)
        {
            cartTotal += cart.items[i].Price * cart.items[i].Quantity;
        }
        cartTotal += cartTotal*salesTax;
        return cartTotal;
    }
}

OrderTotalメソッド(したがってOrderクラス)がCartContentsクラスとCartEntryクラスの実装の詳細に依存することに注意してください。割引を許可するためにこのロジックを変更しようとすると、おそらく3つのクラスすべてを変更する必要があります。また、アイテムを追跡するためにリストコレクションの使用に変更する場合は、Orderクラスも変更する必要があります。

次に、同じことを行うためのわずかに良い方法を示します。

結合の少ない例:

public class CartEntry
{
    public float Price;
    public int Quantity;

    public float GetLineItemTotal()
    {
        return Price * Quantity;
    }
}

public class CartContents
{
    public CartEntry[] items;

    public float GetCartItemsTotal()
    {
        float cartTotal = 0;
        foreach (CartEntry item in items)
        {
            cartTotal += item.GetLineItemTotal();
        }
        return cartTotal;
    }
}

public class Order
{
    private CartContents cart;
    private float salesTax;

    public Order(CartContents cart, float salesTax)
    {
        this.cart = cart;
        this.salesTax = salesTax;
    }

    public float OrderTotal()
    {
        return cart.GetCartItemsTotal() * (1.0f + salesTax);
    }
}

カート品目またはカートコレクションまたは注文の実装に固有のロジックは、そのクラスのみに制限されます。したがって、他のクラスを変更することなく、これらのクラスの実装を変更できます。設計を改善したり、インターフェースを導入したりすることで、このデカップリングをさらに進めることができますが、あなたはポイントを見ると思います。

168
Wedge

iPodsiPadsなどの多くの統合製品(特にApple製)は、密結合の良い例です:バッテリーが切れたら、新しいデバイスを購入することもできますはんだ付けされて固定され、緩むことがないため、交換が非常に高価になります。疎結合プレーヤーでは、バッテリーを簡単に交換できます。

同じはソフトウェア開発にも当てはまります:一般的に(はるかに)疎結合されたコードを使用して、拡張と置換を容易にします(そして個々の部分をより簡単にします)わかる)。しかし、いくつかのモジュールを緊密に統合することで最適化が改善されるため、特別な状況では密結合が有利になることはほとんどありません。

152
Konrad Rudolph

例としてJavaを使用します。次のようなクラスがあるとしましょう:

public class ABC
{
   public void doDiskAccess() {...}
}

クラスを呼び出すとき、次のようにする必要があります。

ABC abc = new ABC();

abc. doDiskAccess();

ここまでは順調ですね。ここで、次のような別のクラスがあるとしましょう。

public class XYZ
{
   public void doNetworkAccess() {...}
}

ABCとまったく同じように見えますが、ディスク上ではなくネットワーク上で動作するとしましょう。それでは、次のようなプログラムを作成しましょう。

if(config.isNetwork()) new XYZ().doNetworkAccess();
else new ABC().doDiskAccess();

それは機能しますが、少し扱いに​​くいです。次のようなインターフェイスでこれを簡素化できます。

public interface Runnable
{
    public void run();
}

public class ABC implements Runnable
{
   public void run() {...}
}

public class XYZ implements Runnable
{
   public void run() {...}
}

これで、私のコードは次のようになります。

Runnable obj = config.isNetwork() ? new XYZ() : new ABC();

obj.run();

それがどれほどクリーンでシンプルなのか理解してください。疎結合の最初の基本原則である抽象化を理解しました。ここからの鍵は、ABCとXYZがそれらを呼び出すクラスのメソッドや変数に依存しないようにすることです。これにより、ABCとXYZを完全に独立したAPIにすることができます。または、言い換えると、親クラスから「分離」または「疎結合」されています。

しかし、2つの間の通信が必要な場合はどうでしょうか。それでは、 Event Model などの抽象化を使用して、親コードが作成したAPIと結合する必要がないようにすることができます。

53
64BitBob

申し訳ありませんが、「疎結合」はコーディングの問題ではなく、設計の問題です。 「緩い結合」という用語は、「高い凝集力」の望ましい状態に密接に関連しており、反対ではあるが相補的です。

疎結合とは、個々の設計要素を構築して、他の設計要素について知る必要がある不要な情報の量を減らすことを意味します。

高凝集度とは「密結合」のようなものですが、高凝集度とは、互いを本当に知る必要がある設計要素が、きれいにエレガントに連携するように設計された状態です。

要は、一部の設計要素は他の設計要素に関する詳細を知っている必要があるため、偶然ではなく、そのように設計する必要があります。他の設計要素は、他の設計要素に関する詳細を知らないようにする必要があります。したがって、ランダムではなく、意図的にそのように設計する必要があります。

これを実装することは、読者のための演習として残されています。

37
David M. Karr

密結合コードは、具体的な実装に依存しています。コードに文字列のリストが必要で、このように宣言する場合(Javaで)

ArrayList<String> myList = new ArrayList<String>();

それから、ArrayListの実装に依存しています。

それを疎結合コードに変更する場合は、参照をインターフェイス(またはその他の抽象)型にします。

List<String> myList = new ArrayList<String>();

これにより、ArrayList実装に固有のmyListanyメソッドを呼び出すことができなくなります。 Listインターフェースで定義されたメソッドのみに制限されています。後でLinkedListが本当に必要であると判断した場合は、ArrayListメソッドを呼び出した100か所ではなく、新しいリストを作成した1か所でコードを変更するだけで済みます。

もちろん、can最初の宣言を使用してArrayListをインスタンス化し、Listインターフェースの一部ではないメソッドを使用しないようにしますが、2番目の宣言を使用すると、コンパイラーは正直になります。

33
Bill the Lizard

ここでの回答の違いの程度は、なぜ理解するのが難しい概念であるかを示していますが、私が説明できる限り簡単にそれを置くことです

私があなたにボールを投げたら、あなたはそれをキャッチできることを知るために、私は本当にあなたが何歳であるかを知る必要はありません。私はあなたが朝食に何を食べたかを知る必要はありません、そして、私はあなたの最初のクラッシュが誰であったか本当に気にしません。私が知る必要があるのは、あなたがキャッチできることです。私がこれを知っていれば、私はあなたかあなたの兄弟にボールを投げているかどうか気にしません。

C#やJavaなどのような非動的言語では、インターフェイスを介してこれを実現します。次のインターフェイスがあるとします。

public ICatcher
{
   public void Catch();
}

そして、次のクラスがあるとしましょう。

public CatcherA : ICatcher
{
   public void Catch()
   {
      console.writeline("You Caught it");
   }

}
public CatcherB : ICatcher
{
   public void Catch()
   {
      console.writeline("Your brother Caught it");
   }

}

現在、CatcherAとCatcherBの両方がCatchメソッドを実装しているため、Catcherを必要とするサービスはこれらのいずれかを使用できますが、実際にはどちらを使用するかは気にしません。したがって、密結合サービスは、キャッチされたサービスを直接インスタンス化する可能性があります。

public CatchService
{
   private CatcherA catcher = new CatcherA();

   public void CatchService()
   {
      catcher.Catch();
   }

}

そのため、CatchServiceは意図したとおりの処理を実行できますが、CatcherAを使用し、常にCatcherAを使用します。ハードコーディングされているため、誰かが来てリファクタリングするまでそこにとどまります。

次に、依存性注入と呼ばれる別のオプションを使用します。

public CatchService
{
   private ICatcher catcher;

   public void CatchService(ICatcher catcher)
   {
      this.catcher = catcher;
      catcher.Catch();
   }
}

したがって、CatchServiceをインスタンス化するcalssは次のことを実行できます。

CatchService catchService = new CatchService(new CatcherA());

または

CatchService catchService = new CatchService(new CatcherB());

これは、CatchサービスがCatcherAまたはCatcherBのいずれにも緊密に結合されていないことを意味します。

IoCフレームワークの使用など、このような疎結合サービスには他にもいくつかの戦略があります。

25
Owen

(密または疎)結合は、文字通り、特定のクラスを別のクラスへの依存から分離するのにかかる労力の量であると考えることができます。たとえば、クラスのすべてのメソッドの最後にLog4Netを呼び出してログを記録する最後のブロックが少しある場合、クラスはLog4Netに密結合されていると言えます。クラスにLog4Netコンポーネントを呼び出す唯一の場所であるLogSomethingという名前のプライベートメソッドが含まれている場合(そして、代わりにすべてLogSomethingと呼ばれる他のメソッド)、クラスはLog4Netに疎結合されていると言います(あまり必要ないため) Log4Netを引き出して別のものに置き換える努力)。

23
MusiGenesis

定義

基本的に、カップリングとは、特定のオブジェクトまたはオブジェクトのセットが、そのタスクを達成するために別のオブジェクトまたはオブジェクトの別のセットにどれだけ依存しているかです。

高結合

車のことを考えてください。エンジンを始動するには、キーをイグニッションに挿入し、回転させ、ガソリンが存在し、sparkが発生し、ピストンが作動し、エンジンが作動する必要があります。車のエンジンは他のいくつかのオブジェクトと高度に結合していると言えます。これは高い結合ですが、実際には悪いことではありません。

ゆるい結合

ユーザーが特定の種類の情報を投稿、編集、表示できるようにするWebページのユーザーコントロールを考えてください。単一のコントロールを使用して、ユーザーが新しい情報を投稿したり、新しい情報を編集したりできます。コントロールは、2つの異なるパス(新規および編集)で共有できる必要があります。コントロールが、それを含むページからの何らかのタイプのデータを必要とするような方法で書かれている場合、それは非常に結合されていると言うことができます。コントロールは、コンテナページから何も必要とすべきではありません。

12
Tom

これは非常に一般的な概念であるため、コード例では全体像を示すことはできません。

仕事中のある人が私に言った、「パターンはフラクタルのようなものです。本当に近くにズームインしたとき、そして建築レベルにズームアウトしたときに見ることができます。」

ウィキペディアの短いページを読むと、この一般性の感覚が得られます。

http://en.wikipedia.org/wiki/Loose_couple

特定のコード例に関しては...

これは、Microsoft.Practices.CompositeUIスタッフから最近作業した疎結合の1つです。

    [ServiceDependency]
    public ICustomizableGridService CustomizableGridService
    {
        protected get { return _customizableGridService; }
        set { _customizableGridService = value; }
    }

このコードは、このクラスがCustomizableGridServiceに依存していることを宣言しています。サービスの正確な実装を直接参照する代わりに、そのサービスの一部の実装が必要であると単純に述べています。その後、実行時に、システムはその依存関係を解決します。

それが明確でない場合、ここでより詳細な説明を読むことができます:

http://en.wikipedia.org/wiki/Dependency_injection

ABCCustomizableGridServiceが、ここで接続する予定の実装であると想像してください。

必要に応じて、それをヤンクして、XYZCustomizableGridServiceまたはStubCustomizableGridServiceに置き換えることができます。この依存関係を持つクラスはまったく変更されません。

ABCCustomizableGridServiceを直接参照していた場合、別のサービス実装で交換するために、その参照を変更する必要があります。

7
rice

カップリングは、コードモジュール(関数、ファイル、またはクラス)、パイプラインのツール、サーバー/クライアントプロセスなど、システム間の依存関係に関係しています。 1つのシステムを変更するには、依存する他のシステムを変更する必要があるため、依存関係が一般的でないほど、より密接に結合されます。理想的な状況は、1つのシステムを変更することができ、それに依存するシステムが変更なしで機能し続ける「疎結合」です。

疎結合を実現する一般的な方法は、明確に定義されたインターフェースを使用することです。 2つのシステム間の相互作用が適切に定義され、両側で順守されている場合、規則が破られないことを保証しながら、1つのシステムを変更することが容易になります。実際には、明確に定義されたインターフェイスが確立されず、ずさんなデザインと密結合が発生することがよくあります。

いくつかの例:

  • アプリケーションはライブラリに依存します。密結合では、アプリは新しいバージョンのlibで中断します。 「DLL Hell」のGoogle。

  • クライアントアプリはサーバーからデータを読み取ります。密結合下では、サーバーへの変更にはクライアント側での修正が必要です。

  • 2つのクラスは、オブジェクト指向の階層で相互作用します。密結合の下で、1つのクラスを変更するには、一致するように他のクラスを更新する必要があります。

  • 複数のコマンドラインツールがパイプで通信します。それらが密結合している場合、1つのコマンドラインツールのバージョンを変更すると、その出力を読み取るツールでエラーが発生します。

6
Parappa

カップリングとは、異なるクラスがどれだけ緊密に接続されているかを指します。密結合クラスには、多数の相互作用と依存関係が含まれます。

疎結合クラスは、互いの依存関係を最小限に抑え、代わりに相互に明確に定義されたパブリックインターフェイスに依存するという点で反対です。

レゴ、SNAPが一緒になっているおもちゃは、ピースを一緒にスナップして任意のシステムを構築できるため、疎結合と見なされます。ただし、ジグソーパズルには密接に結合されたピースがあります。 1つのジグソーパズル(システム)からピースを取り出して別のパズルにスナップすることはできません。システム(パズル)は特定の「デザイン」に固有に構築された非常に特定のピースに大きく依存しているためです。レゴは、より一般的な方法で構築されているため、レゴハウスまたは私のレゴエイリアンマンで使用できます。

リファレンス: https://megocode3.wordpress.com/2008/02/14/couple-and-cohesion/

5
user1263981

2つのコンポーネントは、互いの具体的な実装に依存している場合、高度に結合されます。

私のクラスのメソッドのどこかにこのコードがあると仮定します:

this.some_object = new SomeObject();

現在、私のクラスはSomeObjectに依存しており、それらは高度に結合されています。一方、メソッドInjectSomeObjectがあるとします。

void InjectSomeObject(ISomeObject so) { // note we require an interface, not concrete implementation
  this.some_object = so;
}

次に、最初の例では、挿入されたSomeObjectを使用できます。 これはテスト中に役立ちます。通常の操作では、データベースを使用する、ネットワークを使用する重いクラスなどを使用できます。実装。 密結合コードではそれはできません。

依存性注入コンテナーを使用して、この作業の一部を簡単にすることができます。 WikipediaでDIの詳細を読むことができます: http://en.wikipedia.org/wiki/Dependency_injection

これはあまりにも簡単に理解できる場合があります。ある時点で物事を具体化する必要があります。そうしないと、プログラムが読みにくくなり、理解しにくくなります。そのため、主にコンポーネントの境界でこの手法を使用し、何をしているかを理解してください。疎結合を利用していることを確認してください。そうでない場合は、おそらくその場所では必要ありません。 DIはプログラムをより複雑にする可能性があります。良いトレードオフを確認してください。言い換えれば、良いバランスを維持してください。 システムを設計するときはいつものように。がんばって!

4
Paweł Hajdan

コンピューターサイエンスでは、他の誰もここに投稿していない「疎結合」の別の意味があるので、ここに行きます。確かに私の答えの主題は、質問に対する包括的な答えに属します...

「疎結合」という用語は、マルチCPU構成のCPUアーキテクチャに関する形容詞として使用される用語として最初にコンピューティングに入りました。対応する用語は「密結合」です。疎結合は、CPUが多くのリソースを共有していない場合であり、密結合は共有している場合です。

ここでは「システム」という用語はわかりにくいので、状況を注意深く分析してください。

常にではありませんが、通常、1つのシステム(個々の「PC」ボックスなど)内に存在するハードウェア構成の複数のCPUは密結合されます。 「システム」全体でメインメモリを実際に共有するサブシステムを持つ一部の超高性能システムを除き、すべての分割可能なシステムは疎結合です。

「密結合」と「疎結合」という用語が導入されましたbeforeマルチスレッドおよびマルチコアCPUが発明されました。そして実際、今日では、1つのシステム全体に両方のタイプを含むシステムがあります。現在のソフトウェアシステムに関しては、2つの共通のアーキテクチャがあり、それぞれがさまざまなものの1つです。

第一に、それが問題に関するものであったため、疎結合システムのいくつかの例:

  • VaxClusters
  • Linuxクラスター

対照的に、いくつかの密結合の例:

  • Semetrical-Multi-Processing(SMP)オペレーティングシステム-例Fedora 9
  • マルチスレッドCPU
  • マルチコアCPU

今日のコンピューティングでは、両方が単一のシステム全体で動作する例は珍しくありません。たとえば、Fedora 9を実行する最新のPentiumデュアルまたはクアッドコアCPUを使用します。これらは密結合コンピューティングシステムです。次に、それらのいくつかを疎結合Linux Clusterに結合すると、疎結合コンピューティングと疎結合コンピューティングの両方が実行されます。ああ、現代のハードウェアは素晴らしいものではありません!

3
Richard T

単純な言語では、疎結合とは、発生する他のイベントに依存しないことを意味します。独立して実行されます。

3
Ravindra Miyani

FormAとFormBを備えたWindowsアプリを検討してください。 FormAはプライマリフォームであり、FormBを表示します。 FormBが親にデータを返す必要があると想像してください。

これをした場合:

class FormA 
{
    FormB fb = new FormB( this );

    ...
    fb.Show();
}

class FormB 
{
    FormA parent;

    public FormB( FormA parent )
    {
        this.parent = parent;
    }     
}

FormBはFormAに密結合されています。 FormBには、FormAタイプの親以外の親を含めることはできません。

一方、FormBでイベントを発行し、FormAでそのイベントをサブスクライブさせた場合、FormBはそのイベントを通じて、そのイベントが持つサブスクライバーにデータをプッシュバックできます。この場合、FormBは親との対話を知りません。疎結合を通じて、イベントは単にサブスクライバーと話しているだけです。これで、任意のタイプをFormAの親にできます。

rp

3
rp.

私は非常に簡単なコードカップリングのテストを提案します:

  1. 正確性を保つためにピースAの変更を強制するピースBの変更が存在する場合、コードのピースAはコードのピースBに密結合されます。

  2. ピースAに変更を加える可能性のある変更がピースBにない場合、コードAはコードBと密結合していません。

これは、コードの断片間の結合の程度を確認するのに役立ちます。その理由については、このブログの投稿を参照してください: 約/

2
Marek Dec

他のクラスでnewキーワードを使用してクラスのオブジェクトを作成する場合、実際には密結合(悪い習慣)を行っているので、代わりに疎結合を使用する必要があります。

--- A.Java ---

package interface_package.loose_coupling;

public class A {

void display(InterfaceClass obji)
{
    obji.display();
    System.out.println(obji.getVar());
}
}

--- B.Java ---

package interface_package.loose_coupling;

public class B implements InterfaceClass{

private String var="variable Interface";

public String getVar() {
    return var;
}

public void setVar(String var) {
    this.var = var;
}

@Override
public void display() {
    // TODO Auto-generated method stub
    System.out.println("Display Method Called");
}
}

--- InterfaceClass ---

package interface_package.loose_coupling;

public interface InterfaceClass {

void display();
String getVar();
}

--- MainClass ---

package interface_package.loose_coupling;

public class MainClass {

public static void main(String[] args) {
    // TODO Auto-generated method stub

    A obja=new A();
    B objb=new B();
    obja.display(objb);     //Calling display of A class with object of B class 

}
}

説明:

上記の例では、2つのクラスAとBがあります

クラスBは、インターフェース、つまりInterfaceClassを実装します。

InterfaceClassにはBクラスの抽象メソッドがあり、Aなどの他のクラスからアクセスできるため、InterfaceClassはBクラスのコントラクトを定義します。

クラスAには、InterfaceClassを実装するクラスのオブジェクト(この場合はBクラス)を除外できる表示メソッドがあります。そして、そのオブジェクトで、クラスAのメソッドはクラスBのdisplay()およびgetVar()を呼び出しています

MainClassでは、クラスAとBのオブジェクトを作成しました。Bクラスのオブジェクト、つまりobjbを渡すことで、Aの表示メソッドを呼び出します。 Aの表示メソッドは、Bクラスのオブジェクトで呼び出されます。

次に、疎結合について説明します。将来、クラスBの名前をABCに変更する必要がある場合、クラスBの表示メソッドで名前を変更する必要はなく、新しいオブジェクト(ABCクラス)を作成してMailClassの表示メソッドに渡すだけであるとします。クラスAで何も変更する必要はありません。

参照: http://p3lang.com/2013/06/loose-couple-example-using-interface/

2

ここでいくつかの長い答え。ただし、原理は非常に単純です。 wikipedia からオープニングステートメントを提出します:

「疎結合は、ある種の交換関係を持つ2つ以上のシステムまたは組織間の回復力のある関係を表します。

トランザクションの各端は、その要件を明示的にし、もう一方の端についてはほとんど仮定しません。」

2
Ben

"疎結合" の一般的な概念について詳しく読むことができます。

要するに、2つのクラス間の関係の説明です。各クラスは他のクラスについてほとんど知らないため、各クラスは、他のクラスが存在するかどうかに関係なく、他のクラスの特定の実装に依存せずに正常に機能し続ける可能性がありますクラス。

0
Franci Penov