オブジェクト指向のパラダイムにおける疎結合と密結合の違いは何ですか?


回答:


338

密結合とは、クラスのグループが互いに大きく依存している場合です。

このシナリオは、クラスが担当する責任が多すぎる場合、または1つの懸念が独自のクラスではなく多くのクラスに分散している場合に発生します。

疎結合は、単一の責任と懸念の分離を促進する設計によって実現されます。

疎結合クラスは、他の(具体的な)クラスとは独立して消費およびテストできます。

インターフェイスは、分離に使用する強力なツールです。クラスは、他の具象クラスではなく、インターフェースを介して通信できます。インターフェースを実装するだけで、どのクラスも通信の反対側に置くことができます。

密結合の例:

class CustomerRepository
{
    private readonly Database database;

    public CustomerRepository(Database database)
    {
        this.database = database;
    }

    public void Add(string CustomerName)
    {
        database.AddRow("Customer", CustomerName);
    }
}

class Database
{
    public void AddRow(string Table, string Value)
    {
    }
}

疎結合の例:

class CustomerRepository
{
    private readonly IDatabase database;

    public CustomerRepository(IDatabase database)
    {
        this.database = database;
    }

    public void Add(string CustomerName)
    {
        database.AddRow("Customer", CustomerName);
    }
}

interface IDatabase
{
    void AddRow(string Table, string Value);
}

class Database implements IDatabase
{
    public void AddRow(string Table, string Value)
    {
    }
}

ここに別の例があります


これまでのところ、あなたの言っていることは完全に理にかなっています。カップリングメカニズムがオブザーバーパターンとどのように関係しているのか説明していただけますか?
ジム

1
オブザーバーパターンについては、en.wikipedia.org / wiki / Observer_patternで説明しています。Subjectクラスは、「Observer」から継承するクラスのリストを維持できるため、これらのクラスの具体的なタイプを実際に知らなくても、これは疎結合のインスタンスです。対象はそのオブザーバーやその内部の懸念に依存していません。オブザーバーはサブジェクトやその懸念に依存しません。
ジョナサン

4
Javaインターフェースは、これを支援できるツールです。ただし、必須ではありません。実装ではなくインターフェイスへのプログラムの概念は、パブリックメソッド/属性(または言語がCなどのアクセス修飾子をサポートしていない場合に外部の呼び出し元が使用することを意図したもの)へのプログラムを意味します。インターフェースは変更しないことにより、この暗黙の契約を尊重することになっています。アクセス修飾子を持たない言語(Cなど)では、公開された関数をサポートするために必要に応じて変更される可能性があるため、公開されたインターフェイス/関数のみを使用し、内部で使用されるものをヒットしないことを意味します。
Bill Rosmus

3
@jonathanconwayありがとうございますが、両方のコードは同じことを行います。それらの違いは何ですか?つまり、疎結合にはどのような利点がありますか?
BKSpurgeon

1
私はこの例を(基本的に)以前に見たことがあります。私の意見では、使用される予定のデータベースの種類が多かった場合にのみ問題になるとは思いません。1つだけにする予定の場合、実装を直接使用するのは悪いことでしょう。その後、異なる型を挿入する必要がある場合は、インターフェイスを使用するためにデータベースの1回の使用をリファクタリングしますか?おそらく不要なボイラープレートコードを追加するのではなく、
Carlos Bribiescas 2017

178

コードなしの説明

要約例:

帽子は体に「疎結合」されています。つまり、人/体に変更を加えることなく、簡単に帽子を脱ぐことができます。それができると、「疎結合」になります。詳細については、以下を参照してください。

帽子は体に「疎結合」されています。 これは、人/体に変更を加えることなく、簡単に脱いで脱帽できることを意味します。 画像の帰属:https://pixabay.com/en/greeting-cylinder-chapeau-dignity-317250/

密結合(詳細な例)

あなたの肌について考えてください。体にくっついています。手袋のようにフィットします。しかし、肌の色を白から緑に変えたいとしたらどうでしょうか。皮膚を剥がし、染め、その後貼り付けるのがどれほど苦しいか想像できますか?それはあなたの体に強く結合されているので、あなたの肌を変えることは困難です。簡単に変更することはできません。これを可能にするには、人間を根本的に再設計する必要があります。

  • キーポイント#1:言い換えると、皮膚を変更したい場合、2つが結合されているため、体のデザインも変更する必要があります

神は良いオブジェクト指向のプログラマーではありませんでした。

疎結合(詳細な例)

今朝の服装を考えてください。あなたは青が好きではありませんか?問題ありません。代わりに赤いシャツを着てください。シャツは肌と同じように実際に体につながっていないため、これを簡単かつ簡単に行うことができます。シャツは、それが起こっている身体を知りませんし、気にしません。つまり、体を変えることなく着替えることができます。

  • それがポイント2です。シャツを交換する場合、体を変更する必要はありません -できる場合は、疎結合です。それができない場合は、密結合しています。

それが一言で言えば基本的なコンセプトです。

なぜこれがすべて重要なのですか?

ソフトウェアは常に変化するため、これは重要です。一般的に言えば、コードを変更することなく、簡単にコードを変更できるようにしたいと考えています。それは難問のように聞こえますが、ご容赦ください。

コーディング時のカップリングの実用例

  • CSV / JSON / DBの例:誰かがJSONなどではなくCSVファイルで出力したい場合、またはMySQLからPostGreSQLに切り替えたい場合は、コードを書き直すことなく、コードを非常に簡単に変更できます。つまり、アプリケーションを特定のデータベース実装(Mysqlなど)や特定の出力(CSVファイルなど)に密結合する必要はありません。ソフトウェアでは避けられないように、変更が生じるからです。それらが来たとき、コードの部分が疎結合されていればはるかに簡単です。

  • 車の部品の例:誰かが自分の車で入れたい場合、それを行うために車全体を再設計する必要はありません。車とそのスペアパーツは、疎結合アーキテクチャの完璧な例です。エンジンをより良いものに交換したい場合は、あまり労力をかけずにエンジンを取り外し、より良いものに交換することができます。車がロールスロイス1234エンジンでのみ動作し、他のエンジンでは動作しない場合-車はそのエンジンに密接に結合されます(ロールスロイス1234)。それはで動作するようにあなたの車のデザインを変更した場合には良いだろう任意のエンジンです。コンポーネントと少し疎結合です。あなたの車がエンジンを全く必要とせずに動くことができればもっと良いでしょう!ある程度のカップリングが発生しますが、できる限りそれを最小化するように努力する必要があります。どうして?要件が変わっても、非常に迅速に高品質のソフトウェアを提供できるはずであり、疎結合によってその目標を達成できるからです。

概要

つまり、疎結合を使用すると、コードを簡単に変更できます。上記の回答は、この時点で読む価値のあるコードを提供します。

ポリモーフィズムとSOLIDの原則

Re:@TimoHuovinenのコメント-疎結合の概念は、ポリモーフィズムの概念と密接に関連しています。シャツや車のパーツの基本的な類推を理解すれば、多態性を理解できるようになります。この時点での最良の方法は、このスレッドの他の回答で推定可能な同僚から提供されたコードサンプルを読むことです。これ以上言うと、情報が多すぎて過負荷になる可能性があります。

画像帰属


8
これは、新しいプログラマにとっては過小評価されている説明です。私たちが
公開して

6
密接に結合しなければならないものもあれば、環境に疎結合しているものもあります。スキンの使用は、密結合の適切なアナロジーではありません。皮膚が体と密接に結合していると考えられる場合、他のすべての部分もそうです。ボディ(全体として)は、適切に機能するために(しっかりと統合された)パーツを備えている必要があります(自然から考えられているように、素晴らしい建築家)。これらのパーツが交換可能なように設計されている場合(帽子をかぶるのと同じくらい簡単)、「人間」の体のまさにその意味はその定義を失います。コメント1/2。
lupchiazoem

6
例を挙げれば、皮膚を交換可能にした場合、頭も新しく交換可能でなければなりません。それが起こった場合、人々はある会合から別の会合まで認識されないかもしれません。車とその部品、コンピューターとその部品などのタイト/ルーズカップリングの良い例は、コンピューターのマウス/キーボードに問題がある場合、コンピューター全体を役に立たなくするのではなく、別の部品に置き換えることができます。そして捨てる。コメント2/2。
lupchiazoem

1
@BKSpurgeon素晴らしい説明!! 説明のために例をとるあなたの方法も本当に良いです。
キラン女子

5
非常に創造的に説明しました。私はこの答えと密接に結びついています。
AliN11

72

オブジェクト指向設計では、結合の量は、あるクラスの設計が別のクラスの設計にどれだけ依存しているかを示します。言い換えれば、クラスAの変更がクラスBの変更に関連する変更を強制する頻度はどれくらいですか?密結合は2つのクラスが一緒に変化することが多いことを意味し、疎結合はそれらがほとんど独立していることを意味します。一般に、テストと保守が容易であるため、疎結合をお勧めします。

Martin Fowlerによるこのペーパー(PDF)が役立つでしょう。


「クラスAの変更は、クラスBの変更に関連してどのくらいの頻度で強制されますか?」上記の文の簡単な例が必要ですか?
Kumaresan Perumal 2017

15

一般に、タイトカップリングは良くありませんが、ほとんどの場合、コードの柔軟性と再利用性が低下するため、変更がはるかに困難になり、テスト性などが妨げられます。

密結合オブジェクトは、お互いについてかなりの知識が必要なオブジェクトであり、通常は他のインターフェイスに大きく依存しています。密結合アプリケーションで1つのオブジェクトを変更するには、他の多くのオブジェクトを変更する必要があります。小さなアプリケーションでは、変更を簡単に識別でき、見落とす可能性が低くなります。しかし、大規模なアプリケーションでは、これらの相互依存関係がすべてのプログラマーに常に認識されているわけではなく、変更を見逃す可能性があります。ただし、疎結合オブジェクトの各セットは他のオブジェクトに依存していません。

つまり、疎結合は、システムのコンポーネント間の相互依存性を減らすことを目的とした設計目標であり、1つのコンポーネントの変更により他のコンポーネントの変更が必要になるリスクを減らすことを目的としています。疎結合は、システムの柔軟性を高め、システムをより保守しやすくし、フレームワーク全体をより「安定」させることを目的とした、より一般的な概念です。

カップリングとは、ある要素が別の要素に持つ直接的な知識の度合いを指します。例えば、AとB、Aが動作を変更した場合にのみ、Bのみが動作を変更する。疎結合システムは、定義可能な要素に簡単に分解できます。


11

2つのオブジェクトが疎結合である場合、それらは相互作用できますが、お互いの知識はほとんどありません。

疎結合の設計により、変更を処理できる柔軟なOOシステムを構築できます。

オブザーバーのデザインパターンは、クラスを疎結合にするための良い例です。ウィキペディアで確認できます。


6

カップリングに関する私のブログ投稿からの抜粋:

タイトカップリングとは:-

上記の定義と同様に、密結合オブジェクトは、他のオブジェクトについて知る必要があるオブジェクトであり、通常は互いのインターフェースに大きく依存しています。

密結合アプリケーションで1つのオブジェクトを変更する場合、他の多くのオブジェクトへの変更が必要になることがよくあります。小さなアプリケーションでは問題なく、変更を簡単に特定できます。しかし、大規模なアプリケーションの場合、これらの相互依存関係は、すべてのコンシューマや他の開発者に常に知られているわけではなく、将来の変更の可能性もたくさんあります。

ショッピングカートのデモコードを見て、密結合を理解しましょう。

namespace DNSLooseCoupling
{
    public class ShoppingCart
    {
        public float Price;
        public int Quantity;

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

    public class ShoppingCartContents
    {
        public ShoppingCart[] items;

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

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

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

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

上記の例の問題

タイトカップリングはいくつかの問題を引き起こします。

ここで、OrderTotal()メソッドは、カートの現在のアイテムの完全な量を提供します。このカートシステムに割引機能を追加する場合。非常に密に結合されているため、すべてのクラスで変更を行う必要があるため、上記のコードで行うのは非常に困難です。


6

疎結合とは、2つのコンポーネント間の依存度が非常に低いことを意味します。
例:GSM SIM

密結合とは、2つのコンポーネント間の依存度が非常に高いことを意味します。
例:CDMAモバイル


5

私が理解している方法では、密結合アーキテクチャーは、疎結合アーキテクチャーと比較して、変更に対して多くの柔軟性を提供しません。

しかし、疎結合アーキテクチャーの場合、メッセージ形式またはオペレーティングプラットフォーム、あるいはビジネスロジックの改良は、もう一方の端に影響を与えません。システムが改造のために停止した場合、当然、もう一方の端はしばらくの間サービスにアクセスできませんが、それ以外の場合、変更されていない端は改造前と同じようにメッセージ交換を再開できます。


5

密結合とは、あるクラスが別のクラスに依存していることを意味します。
疎結合とは、1つのクラスがクラスではなくインターフェイスに依存していることを意味します。

密結合、メソッド内で宣言ハードコーディングされた依存関係があります。
疎結合、我々は代わりにハードコーディングされたの実行時に外部依存合格しなければなりません。(疎結合システムは、クラスとの依存関係を減らすためにインターフェースを使用します。)

たとえば、JSON出力やCSV出力など、2つ以上の方法で出力を送信できるシステムがあります。

密結合

public interface OutputGenerator {
    public void generateOutput();
}

public class CSVOutputGenerator implements OutputGenerator {
    public void generateOutput() {
        System.out.println("CSV Output Generator");
    }
}

public class JSONOutputGenerator implements OutputGenerator {
    public void generateOutput() {
        System.out.println("JSON Output Generator");
    }
}

// In Other Code, we write Output Generator like...
public class Class1 {
    public void generateOutput() {
        // Here Output will be in CSV-Format, because of hard-coded code.
        // This method tightly coupled with CSVOutputGenerator class, if we want another Output, we must change this method.
        // Any method, that calls Class1's generateOutput will return CSVOutput, because Class1 is tight couple with CSVOutputGenerator.
        OutputGenerator outputGenerator = new CSVOutputGenerator();
        output.generateOutput();
    }
}

上記の例で、JSONの出力を変更する場合、Class1はCSVOutputGeneratorクラスと密に結合されているため、コード全体を見つけて変更する必要があります。

疎結合

public interface OutputGenerator {
    public void generateOutput();
}

public class CSVOutputGenerator implements OutputGenerator {
    public void generateOutput() {
        System.out.println("CSV Output Generator");
    }
}

public class JSONOutputGenerator implements OutputGenerator {
    public void generateOutput() {
        System.out.println("JSON Output Generator");
    }
}

// In Other Code, we write Output Generator like...
public class Class1 {
    public void generateOutput(OutputGenerator outputGenerator) {
        // if you want to write JSON, pass object of JSONOutputGenerator (Dependency will be passed externally to this method)
        // if you want to write CSV, pass object of CSVOutputGenerator (Dependency will be passed externally to this method)

        // Due to loose couple with class, we don't need to change code of Class1, because Class1 is loose coupled with CSVOutputGenerator or JSONOutputGenerator class
        // Any method, that calls Class1's generateOutput will desired output, because Class1 does not tight couple with CSVOutputGenerator or JSONOutputGenerator class
        OutputGenerator outputGenerator = outputGenerator;
        output.generateOutput();
    }
}

3

ライブラリを通じて依存性注入を提供する特定のツールがあります。たとえば、.netにはninject Libraryがあります。

あなたはJavaでさらに行っている場合は、春には、この機能を提供します。

疎結合オブジェクトは、コードにインターフェイスを導入することで作成できます。これは、これらのソースが行うことです。

あなたが書いているあなたのコードで言う

Myclass m = new Myclass();

メソッドのこのステートメントは、これに依存していることをmyclass密結合と呼びます。ここで、コンストラクター注入、またはプロパティ注入とインスタンス化オブジェクトを提供すると、疎結合になります。


3

類推を使用してここに多くの良い答えがありますが、職場の友人が私にここで述べたものすべてよりも好きな例を与えてくれました...目とメガネ!

密結合

密結合は目です。視力を固定したいのであれば、眼の移植にかかる費用は非常に高く、かなりのリスクがあります。しかし、デザイナー(人類)がより良い方法を見つけたとしたらどうでしょう。ボディに疎結合されている機能を追加して、簡単に変更できるようにします。(はい..メガネ)

疎結合

基本的なビジョンを壊すことなく、メガネを簡単に交換できます。私は眼鏡を外すことができ、私のビジョンはそれが以前と同じようになります(良くも悪くもありません)。異なるメガネを使用すると、リスクがほとんどなく、メンテナンスが容易なため、目を通して世界を見る方法が変わります。

概要

それで、次回誰かが「私のコードが密結合されているかどうか誰が気にするのですか?」答えはすべて、変更する努力、維持する努力、変更のリスクについてです。

では、これはC#でどのように行われるのでしょうか。インターフェースと依存性注入!

編集

これは、デコレータパターンの良い例でもあります。目は、インターフェイスの要件を満たし、さまざまな機能(サングラス、老眼鏡、宝石商用の拡大鏡など)を提供することで装飾しているクラスです。


2

疎結合とは、古いスタイルのハードコードされた依存関係と、何か変更があった場合の頻繁な再コンパイルやコードの再利用などの関連する問題への回答です。コンポーネントにワーカーロジックを実装し、ソリューション固有のワイヤーアップコードをそこで回避することに重点を置いています。

Loose Coupling = IoC簡単な説明 については、こちらをご覧ください。


3
疎結合が制御の反転と同じだとは思いません。制御の反転は、デザインの結合を減らすための非常に便利な手法ですが、他にも多くの手法があります。
Don Kirkby、

2

疎結合とは、依存関係のすべての情報を提供せずに、クラスが必要とする依存関係を間接的に提供するプロセスです(つまり、インターフェイスのfromで)。緊密な結合が依存関係に直接与える場合、コーディングの良い方法ではありません。


2

それは、疎結合では非常に低く、密結合では非常に高い、他のクラスへのクラス依存率に関するものです。サービス指向アーキテクチャーで明確にするために、サービスは、クラスへの依存関係が意図的であるモノリシックに対して相互に疎結合されています


1

オブジェクトの作成/存在が調整できない別のオブジェクトに依存している場合、その密結合。そして、依存関係を調整できる場合、疎結合です。Javaの例を考えます。

class Car {

    private Engine engine = new Engine( "X_COMPANY" ); // this car is being created with "X_COMPANY" engine
    // Other parts

    public Car() { 
        // implemenation 
    }

}

Carクラスのクライアントは、「X_COMPANY」エンジンのみで作成できます。

この結合を壊して、それを変更する機能を検討してください。

class Car {

    private Engine engine;
    // Other members

    public Car( Engine engine ) { // this car can be created with any Engine type
        this.engine = engine;
    }

}

これで、a Carはタイプで作成できるため、「X_COMPANY」のエンジンに依存しません。

Java固有の注意:デカップリングのためだけにJavaインターフェースを使用することは、適切な設計アプローチではありません。Javaでは、インターフェースには目的があります。デカップリングの動作/利点を本質的に提供するコントラクトとして機能することです。

承認された回答におけるビルロスマスのコメントは、適切な説明があります。


0

密結合とは、クラスとオブジェクトが互いに依存していることを意味します。一般に、密結合はコードの柔軟性と再利用性を低下させるため、通常は適切ではありません。疎結合は、異なるクラスを直接使用するクラスの依存関係を低減することを意味します。

密結合密結合オブジェクトは、他のオブジェクトについて知る必要があるオブジェクトであり、通常は相互のインターフェースに大きく依存しています。密結合アプリケーションで1つのオブジェクトを変更するには、多くの場合、他のいくつかのオブジェクトを変更する必要があります。小さなアプリケーションでは、変更を簡単に識別でき、何かを見落とす可能性が低くなります。しかし、大規模なアプリケーションでは、これらの相互依存関係がすべてのプログラマーに常に知られているわけではなく、変更を見落とす可能性があります。例:

    class A {
       public int a = 0;
       public int getA() {
          System.out.println("getA() method");
          return a;
       }
       public void setA(int aa) {
          if(!(aa > 10))
             a = aa;
       }
    }
    public class B {
       public static void main(String[] args) {
          A aObject = new A();
          aObject.a = 100; // Not suppose to happen as defined by class A, this causes tight coupling.
          System.out.println("aObject.a value is: " + aObject.a);
       }
    }

In the above example, the code that is defined by this kind of implementation uses tight coupling and is very bad since class B knows about the detail of class A, if class A changes the variable 'a' to private then class B breaks, also class A's implementation states that variable 'a' should not be more than 10 but as we can see there is no way to enforce such a rule as we can go directly to the variable and change its state to whatever value we decide.

    Output
    aObject.a value is: 100

Loose Coupling
Loose coupling is a design goal to reduce the inter-dependencies between components of a system with the goal of reducing the risk that changes in one component will require changes in any other component.
Loose coupling is a much more generic concept intended to increase the flexibility of the system, make it more maintainable and makes the entire framework more stable.
Example:

class A {
   private int a = 0;
   public int getA() {
      System.out.println("getA() method");
      return a;
   }
   public void setA(int aa) {
      if(!(aa > 10))
         a = aa;
   }
}
public class B {
   public static void main(String[] args) {
      A aObject = new A();
      aObject.setA(100); // No way to set 'a' to such value as this method call will
                         // fail due to its enforced rule.
      System.out.println("aObject value is: " + aObject.getA());
   }
}

上記の例では、この種の実装で定義されたコードは疎結合を使用しており、クラスBはクラスAを通過して、ルールが適用されている状態を取得する必要があるため推奨されます。クラスAが内部で変更された場合、クラスBは通信の手段としてクラスAのみを使用するため、破損しません。

Output
getA() method
aObject value is: 0
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.