回答:
密結合とは、クラスのグループが互いに大きく依存している場合です。
このシナリオは、クラスが担当する責任が多すぎる場合、または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)
{
}
}
帽子は体に「疎結合」されています。つまり、人/体に変更を加えることなく、簡単に帽子を脱ぐことができます。それができると、「疎結合」になります。詳細については、以下を参照してください。
あなたの肌について考えてください。体にくっついています。手袋のようにフィットします。しかし、肌の色を白から緑に変えたいとしたらどうでしょうか。皮膚を剥がし、染め、その後貼り付けるのがどれほど苦しいか想像できますか?それはあなたの体に強く結合されているので、あなたの肌を変えることは困難です。簡単に変更することはできません。これを可能にするには、人間を根本的に再設計する必要があります。
神は良いオブジェクト指向のプログラマーではありませんでした。
今朝の服装を考えてください。あなたは青が好きではありませんか?問題ありません。代わりに赤いシャツを着てください。シャツは肌と同じように実際に体につながっていないため、これを簡単かつ簡単に行うことができます。シャツは、それが起こっている身体を知りませんし、気にしません。つまり、体を変えることなく着替えることができます。
それが一言で言えば基本的なコンセプトです。
ソフトウェアは常に変化するため、これは重要です。一般的に言えば、コードを変更することなく、簡単にコードを変更できるようにしたいと考えています。それは難問のように聞こえますが、ご容赦ください。
CSV / JSON / DBの例:誰かがJSONなどではなくCSVファイルで出力したい場合、またはMySQLからPostGreSQLに切り替えたい場合は、コードを書き直すことなく、コードを非常に簡単に変更できます。つまり、アプリケーションを特定のデータベース実装(Mysqlなど)や特定の出力(CSVファイルなど)に密結合する必要はありません。ソフトウェアでは避けられないように、変更が生じるからです。それらが来たとき、コードの部分が疎結合されていればはるかに簡単です。
車の部品の例:誰かが自分の車を黒で入れたい場合、それを行うために車全体を再設計する必要はありません。車とそのスペアパーツは、疎結合アーキテクチャの完璧な例です。エンジンをより良いものに交換したい場合は、あまり労力をかけずにエンジンを取り外し、より良いものに交換することができます。車がロールスロイス1234エンジンでのみ動作し、他のエンジンでは動作しない場合-車はそのエンジンに密接に結合されます(ロールスロイス1234)。それはで動作するようにあなたの車のデザインを変更した場合には良いだろう任意のエンジンです。コンポーネントと少し疎結合です。あなたの車がエンジンを全く必要とせずに動くことができればもっと良いでしょう!ある程度のカップリングが発生しますが、できる限りそれを最小化するように努力する必要があります。どうして?要件が変わっても、非常に迅速に高品質のソフトウェアを提供できるはずであり、疎結合によってその目標を達成できるからです。
つまり、疎結合を使用すると、コードを簡単に変更できます。上記の回答は、この時点で読む価値のあるコードを提供します。
Re:@TimoHuovinenのコメント-疎結合の概念は、ポリモーフィズムの概念と密接に関連しています。シャツや車のパーツの基本的な類推を理解すれば、多態性を理解できるようになります。この時点での最良の方法は、このスレッドの他の回答で推定可能な同僚から提供されたコードサンプルを読むことです。これ以上言うと、情報が多すぎて過負荷になる可能性があります。
画像帰属。
オブジェクト指向設計では、結合の量は、あるクラスの設計が別のクラスの設計にどれだけ依存しているかを示します。言い換えれば、クラスAの変更がクラスBの変更に関連する変更を強制する頻度はどれくらいですか?密結合は2つのクラスが一緒に変化することが多いことを意味し、疎結合はそれらがほとんど独立していることを意味します。一般に、テストと保守が容易であるため、疎結合をお勧めします。
Martin Fowlerによるこのペーパー(PDF)が役立つでしょう。
一般に、タイトカップリングは良くありませんが、ほとんどの場合、コードの柔軟性と再利用性が低下するため、変更がはるかに困難になり、テスト性などが妨げられます。
密結合オブジェクトは、お互いについてかなりの知識が必要なオブジェクトであり、通常は他のインターフェイスに大きく依存しています。密結合アプリケーションで1つのオブジェクトを変更するには、他の多くのオブジェクトを変更する必要があります。小さなアプリケーションでは、変更を簡単に識別でき、見落とす可能性が低くなります。しかし、大規模なアプリケーションでは、これらの相互依存関係がすべてのプログラマーに常に認識されているわけではなく、変更を見逃す可能性があります。ただし、疎結合オブジェクトの各セットは他のオブジェクトに依存していません。
つまり、疎結合は、システムのコンポーネント間の相互依存性を減らすことを目的とした設計目標であり、1つのコンポーネントの変更により他のコンポーネントの変更が必要になるリスクを減らすことを目的としています。疎結合は、システムの柔軟性を高め、システムをより保守しやすくし、フレームワーク全体をより「安定」させることを目的とした、より一般的な概念です。
カップリングとは、ある要素が別の要素に持つ直接的な知識の度合いを指します。例えば、AとB、Aが動作を変更した場合にのみ、Bのみが動作を変更する。疎結合システムは、定義可能な要素に簡単に分解できます。
カップリングに関する私のブログ投稿からの抜粋:
タイトカップリングとは:-
上記の定義と同様に、密結合オブジェクトは、他のオブジェクトについて知る必要があるオブジェクトであり、通常は互いのインターフェースに大きく依存しています。
密結合アプリケーションで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()
メソッドは、カートの現在のアイテムの完全な量を提供します。このカートシステムに割引機能を追加する場合。非常に密に結合されているため、すべてのクラスで変更を行う必要があるため、上記のコードで行うのは非常に困難です。
疎結合とは、2つのコンポーネント間の依存度が非常に低いことを意味します。
例:GSM SIM
密結合とは、2つのコンポーネント間の依存度が非常に高いことを意味します。
例:CDMAモバイル
密結合とは、あるクラスが別のクラスに依存していることを意味します。
疎結合とは、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();
}
}
ライブラリを通じて依存性注入を提供する特定のツールがあります。たとえば、.netにはninject Libraryがあります。
あなたはJavaでさらに行っている場合は、春には、この機能を提供します。
疎結合オブジェクトは、コードにインターフェイスを導入することで作成できます。これは、これらのソースが行うことです。
あなたが書いているあなたのコードで言う
Myclass m = new Myclass();
メソッドのこのステートメントは、これに依存していることをmyclass
密結合と呼びます。ここで、コンストラクター注入、またはプロパティ注入とインスタンス化オブジェクトを提供すると、疎結合になります。
類推を使用してここに多くの良い答えがありますが、職場の友人が私にここで述べたものすべてよりも好きな例を与えてくれました...目とメガネ!
密結合
密結合は目です。視力を固定したいのであれば、眼の移植にかかる費用は非常に高く、かなりのリスクがあります。しかし、デザイナー(人類)がより良い方法を見つけたとしたらどうでしょう。ボディに疎結合されている機能を追加して、簡単に変更できるようにします。(はい..メガネ)
疎結合
基本的なビジョンを壊すことなく、メガネを簡単に交換できます。私は眼鏡を外すことができ、私のビジョンはそれが以前と同じようになります(良くも悪くもありません)。異なるメガネを使用すると、リスクがほとんどなく、メンテナンスが容易なため、目を通して世界を見る方法が変わります。
概要
それで、次回誰かが「私のコードが密結合されているかどうか誰が気にするのですか?」答えはすべて、変更する努力、維持する努力、変更のリスクについてです。
では、これはC#でどのように行われるのでしょうか。インターフェースと依存性注入!
編集
これは、デコレータパターンの良い例でもあります。目は、インターフェイスの要件を満たし、さまざまな機能(サングラス、老眼鏡、宝石商用の拡大鏡など)を提供することで装飾しているクラスです。
疎結合とは、古いスタイルのハードコードされた依存関係と、何か変更があった場合の頻繁な再コンパイルやコードの再利用などの関連する問題への回答です。コンポーネントにワーカーロジックを実装し、ソリューション固有のワイヤーアップコードをそこで回避することに重点を置いています。
疎結合とは、依存関係のすべての情報を提供せずに、クラスが必要とする依存関係を間接的に提供するプロセスです(つまり、インターフェイスの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;
}
}
これで、a Car
はタイプで作成できるため、「X_COMPANY」のエンジンに依存しません。
Java固有の注意:デカップリングのためだけにJavaインターフェースを使用することは、適切な設計アプローチではありません。Javaでは、インターフェースには目的があります。デカップリングの動作/利点を本質的に提供するコントラクトとして機能することです。
承認された回答におけるビルロスマスのコメントは、適切な説明があります。
密結合とは、クラスとオブジェクトが互いに依存していることを意味します。一般に、密結合はコードの柔軟性と再利用性を低下させるため、通常は適切ではありません。疎結合は、異なるクラスを直接使用するクラスの依存関係を低減することを意味します。
密結合密結合オブジェクトは、他のオブジェクトについて知る必要があるオブジェクトであり、通常は相互のインターフェースに大きく依存しています。密結合アプリケーションで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