オブジェクト指向パラダイムにおける緩い結合と密な結合の正確な違いを説明できる人はいますか?
16 に答える
密結合とは、クラスのグループが相互に大きく依存している場合です。
このシナリオは、クラスがあまりにも多くの責任を引き受ける場合、または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:つまり、肌を変えたい場合は、2つが緊密に結合されているため、体のデザインも変更する必要があります。
神は優れたオブジェクト指向プログラマーではありませんでした。
緩い結合(詳細な例)
今朝服を着ることを考えてください。あなたは青が好きではありませんか?問題ありません。代わりに赤いシャツを着ることができます。シャツは肌と同じように体に接続されていないため、これを簡単かつ簡単に行うことができます。シャツは、それがどの体に起こっているのかを知らないか、気にしません。つまり、体を変えなくても着替えることができます。
- それがポイント2です。あなたがシャツを着替えるなら、あなたはあなたの体を変えることを強制されません-あなたがそれをすることができるとき、あなたは緩い結合を持っています。それができないときは、緊密な結合があります。
それが一言で言えば基本的な考え方です。
なぜこれがすべて重要なのですか?
ソフトウェアを書くとき、変化は避けられません。変更が特定の場所で行われることが事前にわかっている場合は、ソフトウェアがその特定のポイントで緩く結合されていることを確認する必要があります。これにより、バグなしで簡単かつ迅速に変更を行うことができます。 .soそれはどういう意味ですか?いくつかの例を考えてみましょう。
ソフトウェアの緩い結合:
- CSV / JSONの例:キャリアの早い段階で、マネージャーは「出力をCSVファイルとして提供してください」と言っていました。素晴らしい。私はハッキングして、魅力のように機能するルーチンを作成しました。それから1、2週間後、彼は次のように述べています。「実際には、JSONで別のクライアントの出力が必要です」。
「1つの出力をCSVで、もう1つをJSONで出力したいですか?」
"それは正しい"
なんて痛い。全部書き直さなければなりませんでした。知るか?明日、彼は振り返って、「XML形式でそれが欲しい」と言うことができました。
私は彼が別のフォーマットを要求したであろうことを知っていたはずであり、私はそれを計画することができたでしょう。
インターフェイス(緩く結合されたデザインパターン)を使用して全体を書き直し、新しい出力形式を追加したり、変更を加えたりするのがはるかに簡単になりました。CSV出力が壊れることを恐れずにJSON部分を編集できます。
DBの例: MySQLからPostGreSQLに簡単に切り替えたい場合-緩く結合されたコードを使用すると、切り替えが非常に簡単になります(つまり、青いシャツの代わりに赤いシャツを着ます)。Rails ActiveRecordライブラリは、そのデータベース実装で緩く結合されています。これにより、同じコードベースを使用しながら、誰かが独自のデータベース実装を非常に簡単に使用できるようになります。
クラウドプロバイダーの例:または、AWSを使用していて、市場の優位性のために課金が多すぎる場合は、GoogleやAzureなどに簡単に切り替えることができるはずです。これがActiveStorageのようなライブラリが存在する理由です。使用されている特定のクラウドプロバイダー(Azure、AWS S3、GCSなど)に関して健全な無関心を持っているユーザー。1行のコード変更だけでクラウドプロバイダーを簡単に変更できます。クラウドストレージプロバイダーの実装の詳細は、ゆるく結合されています。
テスト:あらかじめ決められた出力と入力を使用してソフトウェアをテストする場合、どのようにテストしますか?緩く結合されたソフトウェアを使用すると、簡単です。テストを実行できます。また、本番コードをデプロイして、すべて同じコードベースで実行することもできます。密結合のコードでは、本番コードをテストすることはほぼ不可能です。
すべてを「緩く結合」する必要がありますか?おそらくそうではありません。私たちは判断を下さなければなりません。コードを書くとき、ある程度の結合は避けられません。ただし、可能であれば、特にどこで変更されるかを事前に知っている場合は、最小化することを検討する必要があります。
概要
つまり、結合が緩いため、コードを簡単に変更できます。
上記の回答は、読む価値のあるコードを提供します。
高度なトピック
緩い結合は、ポリモーフィズムおよびインターフェースと密接に関連しています。より高度なトピックについて読み続けたい場合は、次のようなスタックオーバーフローの回答が役立ちます。
- ポリモーフィズムとは何ですか?
- インターフェイスとは何ですか?
- 「漏れのある抽象化」とはどういう意味ですか?私が書いたものではありません。
(私の答えが紛らわしい場合は、修正できるようにコメントを投稿してください。@ lupchiazoemは、以下に非常にすばらしい返信を書いています。これも読む価値があります。)
オブジェクト指向設計では、結合の量は、あるクラスの設計が別のクラスの設計にどの程度依存するかを示します。言い換えれば、クラスAの変更は、クラスBの変更に関連する変更をどのくらいの頻度で強制しますか?密結合は、2つのクラスが頻繁に一緒に変化することを意味し、緩結合は、それらがほとんど独立していることを意味します。一般に、テストと保守が簡単なため、緩い結合をお勧めします。
Martin Fowlerによるこの論文(PDF)が役立つかもしれません。
一般に、タイトカップリングは悪いですが、ほとんどの場合、コードの柔軟性と再利用性が低下するため、変更がはるかに困難になり、テスト容易性などが妨げられます。
密結合オブジェクトは、相互にかなりの知識が必要なオブジェクトであり、通常、相互のインターフェイスに大きく依存しています。緊密に結合されたアプリケーションで1つのオブジェクトを変更するには、多くの場合、他の多くのオブジェクトを変更する必要があります。小さなアプリケーションでは、変更を簡単に識別でき、何かを見逃す可能性が低くなります。しかし、大規模なアプリケーションでは、これらの相互依存関係がすべてのプログラマーに常に知られているわけではなく、変更を見逃す可能性があります。ただし、緩く結合されたオブジェクトの各セットは、他のオブジェクトに依存していません。
要するに、緩い結合は、あるコンポーネントの変更が他のコンポーネントの変更を必要とするリスクを減らすことを目的として、システムのコンポーネント間の相互依存性を減らすことを目的とした設計目標であると言えます。緩い結合は、システムの柔軟性を高め、システムをより保守しやすくし、フレームワーク全体をより「安定」させることを目的とした、はるかに一般的な概念です。
結合とは、ある要素が別の要素について持っている直接的な知識の程度を指します。たとえば、AとB、Aが動作を変更した場合にのみBのみが動作を変更します。緩く結合されたシステムは、定義可能な要素に簡単に分解できます。
タイトカップリングとは、あるクラスが別のクラスに依存していることを意味します。
緩い結合とは、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();
}
}
2つのオブジェクトが緩く結合されている場合、それらは相互作用できますが、相互の知識はほとんどありません。
ゆるく結合された設計により、変更を処理できる柔軟なOOシステムを構築できます。
オブザーバーデザインパターンは、クラスを疎結合にするための良い例です。ウィキペディアで確認できます。
結合が緩いということは、2つのコンポーネント間の依存度が非常に低いことを意味します。
例:GSM SIM
密結合は、2つのコンポーネント間の依存度が非常に高いことを意味します。
例:CDMAモバイル
カップリングに関する私のブログ投稿からの抜粋:
タイトカップリングとは:-
上記の定義と同様に、密結合オブジェクトは、他のオブジェクトについて知る必要があるオブジェクトであり、通常、互いのインターフェースに大きく依存しています。
密結合のアプリケーションで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()
メソッドはカートの現在のアイテムの完全な金額を提供します。このカートシステムに割引機能を追加したい場合。上記のコードは非常に緊密に結合されているため、すべてのクラスで変更を加える必要があるため、上記のコードで行うのは非常に困難です。
私が理解しているように、密結合のアーキテクチャは、緩く結合されたアーキテクチャと比較した場合、変更に対して多くの柔軟性を提供しません。
ただし、疎結合のアーキテクチャ、メッセージ形式、オペレーティングプラットフォームの場合、またはビジネスロジックの刷新は、もう一方の端に影響を与えません。システムが改造のために停止した場合、もちろん、もう一方の端はしばらくの間サービスにアクセスできなくなりますが、それ以外の場合、変更されていない端は、改造前と同じようにメッセージ交換を再開できます。
アナロジーを使ってここに良い答えがたくさんありますが、職場の友人は私がここで言及されたもののすべてよりも好きだったという例を私に与えました...目と眼鏡!
密結合
緊密な結合が目になるでしょう。視力を直したいのなら、眼球移植を受けるのに非常に費用がかかり、かなりのリスクがあります。しかし、(人類である)設計者がより良い方法を見つけたらどうなるでしょうか。ボディにゆるく結合された機能を追加して、簡単に変更できるようにします。(はい..メガネ)
緩い結合
根底にあるビジョンを壊すことなく、メガネを簡単に交換できます。私は眼鏡を外すことができ、私のビジョンは以前の状態になります(良くも悪くもありません)。さまざまな眼鏡を使用することで、リスクがほとんどなく、保守が容易で、目を通して世界を見る方法が変わります。
概要
それで、次に誰かがあなたに「私のコードが緊密に結合されているかどうかを気にするのは誰か」と尋ねるとき。答えは、変化への努力、維持への努力、そして変化のリスクに関するものです。
では、これはC#でどのように行われるのでしょうか。インターフェースと依存性注入!
編集
これもデコレータパターンの良い例です。目は、インターフェイスの要件を満たしながら、さまざまな機能(サングラス、老眼鏡、宝石商用の拡大鏡など)を提供することで装飾しているクラスです。
ライブラリを介して依存性注入を提供する特定のツールがあります。たとえば、.netにはninjectLibraryがあります。
さらにJavaを使用する場合は、Springがこの機能を提供します。
ゆるく結合されたオブジェクトは、コードにインターフェイスを導入することで作成できます。これは、これらのソースが行うことです。
あなたが書いているあなたのコードで言う
Myclass m = new Myclass();
これで、メソッドのこのステートメントは、これに依存していることをmyclass
密結合と呼びます。ここで、コンストラクタインジェクション、またはプロパティインジェクションとインスタンス化オブジェクトを提供すると、緩く結合されます。
それは、緩く結合されている場合は非常に低く、密に結合されている場合は非常に高い、別のクラスへのクラス依存率に関するものです。サービス指向アーキテクチャーで明確にするために、サービスは、相互に依存するクラスが意図的に存在するモノリシックに対して、互いに緩く結合されています。
緩い結合は、コーディングの良い方法ではない依存関係で直接与える密結合の場合に、依存関係のすべての情報を提供せずに、クラスが間接的に必要とする依存関係を与えるプロセスです(つまり、インターフェイスのfromで)。
オブジェクトの作成/存在が、調整できない別のオブジェクトに依存している場合、その緊密な結合。そして、依存関係を調整できる場合は、その緩い結合。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;
}
}
現在、aCar
はタイプで作成できるため、「X_COMPANY」のエンジンに依存していません。
Java固有の注意:デカップリングのためだけにJavaインターフェースを使用することは、適切な設計アプローチではありません。Javaでは、インターフェースには目的があります。つまり、デカップリング動作/利点を本質的に提供するコントラクトとして機能することです。
受け入れられた答えのビル・ロスムスのコメントは良い説明があります。
緩くて緊密な結合は、あるプログラムコンポーネントから別のプログラムコンポーネントへの依存関係に関するものです。これは、プログラミングクラスだけでなく、システムコンポーネントのプログラミングにも依存していることを意味します。
たとえば、単純な生のSQLクエリのみを使用してSQL Serverからデータを受信する場合、これは緩い結合です。LooseCouplingと単純なrawSQLクエリの反対は、TightCouplingとEntityFrameworkCoreです。Entity Framework Coreでは、コードに反映する必要のあるデータベースの変更よりも、コードにPOCOクラスを使用して完全なモデルを作成する必要があります。
したがって、プログラムコードとデータベース構造の間の緊密な結合はEntity Frameworkです。このアプローチの反対は、ORMの使用を拒否し、プログラムコードに完全なミラーデータベース構造を含めることを拒否します。