「疎結合」の概念が理解できないようです。「ゆるい」という言葉が通常否定的な意味合いを持っていることは役に立たないと思うので、疎結合が良いことであることをいつも忘れています。
誰かがこの概念を説明する「前」と「後」のコード (または疑似コード) を示してくれませんか?
「疎結合」の概念が理解できないようです。「ゆるい」という言葉が通常否定的な意味合いを持っていることは役に立たないと思うので、疎結合が良いことであることをいつも忘れています。
誰かがこの概念を説明する「前」と「後」のコード (または疑似コード) を示してくれませんか?
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;
}
}
メソッド (および Order クラス) がおよびクラスOrderTotal
の実装の詳細にどのように依存するかに注目してください。割引を可能にするためにこのロジックを変更しようとすると、おそらく 3 つのクラスすべてを変更する必要があります。また、コレクションを使用してアイテムを追跡するように変更する場合は、クラスも変更する必要があります。CartContents
CartEntry
List<CartEntry>
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);
}
}
カート ライン アイテム、カート コレクション、または注文の実装に固有のロジックは、そのクラスだけに制限されます。したがって、他のクラスを変更することなく、これらのクラスの実装を変更できます。設計を改善したり、インターフェイスを導入したりすることで、このデカップリングをさらに進めることができますが、要点はお分かりいただけたと思います。
iPodやiPadなどの多くの統合製品 (特に Apple による)は、密結合の良い例です。バッテリーが切れたら、新しいデバイスを購入することもできます。これは、バッテリーがはんだ付けされて固定されていて緩まないため、交換が非常に困難になるためです。高い。疎結合のプレーヤーでは、バッテリーを簡単に交換できます。
同じことがソフトウェア開発にも当てはまります。拡張と置換を容易にする (そして個々の部分を理解しやすくする) ために、コードを疎結合にする方が一般的に (はるかに) 優れています。しかし、ごくまれに、いくつかのモジュールを密に統合することで最適化が向上するため、特別な状況下では密結合が有利になることがあります。
例として 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 つの間の通信が必要な場合はどうすればよいでしょうか。次に、イベント モデルなどのさらなる抽象化を使用して、親コードが作成した API と結合する必要がないようにすることができます。
申し訳ありませんが、「疎結合」はコーディングの問題ではなく、設計の問題です。「疎結合」という用語は、「高結束」の望ましい状態と密接に関連しており、反対ではあるが補完的です。
疎結合とは、単純に、個々の設計要素を構築して、他の設計要素について知る必要のある不要な情報の量を減らすことを意味します。
ハイ・コヒージョンとは「密結合」のようなものですが、ハイ・コヒージョンとは、本当にお互いを知る必要のある設計要素が、きれいにエレガントに連携するように設計されている状態です。
重要なのは、一部の設計要素は他の設計要素の詳細を知っている必要があるため、偶然ではなく、そのように設計する必要があるということです。他の設計要素は、他の設計要素の詳細を認識してはならないため、無作為ではなく意図的にそのように設計する必要があります。
これを実装することは、読者の演習として残されています:)。
密結合コードは具体的な実装に依存します。コードで文字列のリストが必要で、次のように宣言する場合 (Java で)
ArrayList<String> myList = new ArrayList<String>();
それから私は ArrayList 実装に依存しています。
それを疎結合コードに変更したい場合は、参照をインターフェイス (または他の抽象) 型にします。
List<String> myList = new ArrayList<String>();
これにより、ArrayList 実装に固有のメソッドを呼び出すことができなくなります。myList
List インターフェースで定義されているメソッドのみに制限されています。後で LinkedList が本当に必要だと判断した場合、コードを変更する必要があるのは、ArrayList メソッドを呼び出した 100 か所ではなく、新しい List を作成した 1 か所だけです。
もちろん、最初の宣言を使用して ArrayListをインスタンス化し、List インターフェースの一部ではないメソッドを使用しないように制限することはできますが、2 番目の宣言を使用すると、コンパイラはあなたを正直に保ちます。
ここでの回答の違いの程度は、理解するのが難しい概念である理由を示していますが、私が説明できるのと同じくらい簡単に言えば:
私があなたにボールを投げたら、あなたはそれをキャッチできることを私が知るために、あなたが何歳かを知る必要はありません. あなたが朝食に何を食べたかを知る必要はありませんし、あなたの最初の片思いが誰であったかは気にしません。私が知る必要があるのは、あなたがキャッチできるということだけです。私がこれを知っていれば、私があなたにボールを投げているのか、あなたの兄弟にボールを投げているのかは気にしません.
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 を必要とするサービスはこれらのいずれかを使用することができ、実際にどれがどれであるかを気にする必要はありません。したがって、密結合されたサービスは、キャッチされた ie を直接インスタンス化する可能性があります
public CatchService
{
private CatcherA catcher = new CatcherA();
public void CatchService()
{
catcher.Catch();
}
}
したがって、CatchService
は設定したことを正確に実行できますが、使用しCatcherA
、常に user になりますCatcherA
。ハードコードされているため、誰かがやって来てリファクタリングするまでそこにとどまります。
ここで、依存性注入と呼ばれる別のオプションを取りましょう。
public CatchService
{
private ICatcher catcher;
public void CatchService(ICatcher catcher)
{
this.catcher = catcher;
catcher.Catch();
}
}
したがって、インスタンス化する呼び出しCatchService
は次のことを行う可能性があります。
CatchService catchService = new CatchService(new CatcherA());
また
CatchService catchService = new CatchService(new CatcherB());
これは、サービスが または のいずれかCatch
と密結合していないことを意味します。CatcherA
CatcherB
IoC フレームワークの使用など、このようなサービスを疎結合するための戦略は他にもいくつかあります。
(密結合または疎結合) は、文字通り、特定のクラスを別のクラスへの依存から分離するのにかかる労力の量であると考えることができます。たとえば、クラスのすべてのメソッドの下部に、Log4Net を呼び出して何かをログに記録する小さな final ブロックがある場合、そのクラスは Log4Net に密結合されていると言えます。代わりに、Log4Net コンポーネントを呼び出す唯一の場所である LogSomething という名前のプライベート メソッドがクラスに含まれていた場合 (および、代わりにすべての他のメソッドがすべて LogSomething を呼び出していた場合)、クラスは Log4Net に疎結合されていると言えます (それほど時間がかからないため)。 Log4Net を取り出して別のものに置き換える努力をします)。
基本的に、結合とは、特定のオブジェクトまたはオブジェクトのセットが、そのタスクを達成するために、別のオブジェクトまたは別のオブジェクトのセットにどの程度依存しているかです。
車を考えてみてください。エンジンが始動するには、キーをイグニッションに挿入して回し、ガソリンが存在し、火花が発生し、ピストンが点火し、エンジンが始動する必要があります。車のエンジンは、他のいくつかのオブジェクトと高度に結合していると言えます。これは高結合ですが、実際には悪いことではありません。
ユーザーが何らかの種類の情報を投稿、編集、および表示できるようにする Web ページのユーザー コントロールを考えてみてください。単一のコントロールを使用して、ユーザーが新しい情報を投稿したり、新しい情報を編集したりできます。コントロールは、新規と編集の 2 つの異なるパス間で共有できる必要があります。コントロールが含まれるページからの何らかのタイプのデータを必要とするような方法でコントロールが記述されている場合、結合が強すぎると言えます。コントロールは、コンテナー ページから何も必要としません。
カップリングは、コードのモジュール(関数、ファイル、またはクラス)、パイプライン内のツール、サーバークライアントプロセスなどのシステム間の依存関係と関係があります。1つのシステムを変更するには、それに依存する他のシステムを変更する必要があるため、依存関係が一般的でないほど、依存関係は「緊密に結合」されます。理想的な状況は、1つのシステムを変更でき、それに依存するシステムが変更なしで機能し続ける「緩い結合」です。
緩い結合を実現する一般的な方法は、明確に定義されたインターフェイスを使用することです。2つのシステム間の相互作用が明確に定義され、両側で順守されている場合、規則に違反しないようにしながら、1つのシステムを変更することが容易になります。実際には、明確に定義されたインターフェイスが確立されていないことが一般的に発生し、その結果、設計がずさんになり、結合が緊密になります。
いくつかの例:
アプリケーションはライブラリに依存します。緊密な結合の下で、アプリは新しいバージョンのライブラリで壊れます。「DLL地獄」のためのグーグル。
クライアントアプリはサーバーからデータを読み取ります。密結合では、サーバーへの変更にはクライアント側での修正が必要です。
2つのクラスは、オブジェクト指向の階層で相互作用します。密結合では、一方のクラスを変更するには、一致するようにもう一方のクラスを更新する必要があります。
複数のコマンドラインツールがパイプで通信します。それらが緊密に結合されている場合、1つのコマンドラインツールのバージョンを変更すると、その出力を読み取るツールでエラーが発生します。
これはかなり一般的な概念であるため、コード例は全体像を示すものではありません。
ここで働いているある人は私にこう言いました。
簡単なウィキペディアのページを読むと、この一般性を感じることができます。
http://en.wikipedia.org/wiki/Loose_coupling
具体的なコード例に関しては...
これは、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 を直接参照していた場合は、別のサービス実装に交換するために、その/それらの参照に変更を加える必要があります。
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 を使用できます。これは、テスト中に役立ちます。通常の操作では、負荷の高い、データベースを使用する、ネットワークを使用するクラスなどを使用できますが、テストでは軽量のモック実装を渡します。密結合コードでは、それはできません。
依存性注入コンテナーを使用すると、この作業の一部を簡単にすることができます。DI の詳細については、ウィキペディア ( http://en.wikipedia.org/wiki/Dependency_injection ) を参照してください。
これを行き過ぎてしまうのは簡単なことです。ある時点で、物事を具体化する必要があります。そうしないと、プログラムが読みにくく、理解しにくくなります。したがって、この手法は主にコンポーネントの境界で使用し、何をしているのかを理解してください。疎結合を利用していることを確認してください。そうでない場合は、おそらくその場所では必要ありません。DI により、プログラムがより複雑になる場合があります。適切なトレードオフを行うようにしてください。言い換えれば、バランスをよく保つことです。システムを設計するときはいつものように。幸運を!
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
簡単に言えば、疎結合とは、発生する他のイベントに依存しないことを意味します。独立して実行されます。
コンピューター サイエンスでは、「疎結合」には、他の誰もここに投稿していない別の意味があります。それで...ここに行きます。これがヒープの一番下で失われないように、投票していただければ幸いです。確かに、私の回答の主題は、質問に対する包括的な回答に属しています...つまり:
「疎結合」という用語は、マルチ CPU 構成の CPU アーキテクチャに関する形容詞として使用される用語として最初にコンピューティングに登場しました。対応する用語は「密結合」です。疎結合は CPU が多くのリソースを共有しない場合であり、密結合は共有する場合です。
「システム」という用語はここでは混乱を招く可能性があるため、状況を注意深く解析してください。
常にではありませんが、通常、1 つのシステム内に複数の CPU が存在するハードウェア構成 (個々の「PC」ボックスなど) では、複数の CPU が密結合されます。「システム」間でメイン メモリを実際に共有するサブシステムを持つ一部の超高性能システムを除いて、分割可能なすべてのシステムは疎結合です。
密結合と疎結合という用語は、マルチスレッドおよびマルチコア CPU が発明される前に導入されたため、今日の状況を完全に表現するには、これらの用語に関連する用語が必要になる場合があります。そして実際、今日では、両方のタイプを 1 つの全体的なシステムに含めるシステムを十分に持つことができます。現在のソフトウェア システムに関しては、それぞれに 1 つずつ、2 つの一般的なアーキテクチャがあります。
まず、それが質問の内容だったので、疎結合システムの例をいくつか示します。
対照的に、いくつかの密結合の例:
今日のコンピューティングでは、単一のシステム全体で両方が動作する例は珍しくありません。たとえば、Fedora 9 を実行する最新の Pentium デュアルまたはクアッド コア CPU を考えてみましょう。これらは密結合のコンピューティング システムです。次に、それらのいくつかを疎結合の Linux クラスターに結合すると、疎結合と密結合の両方のコンピューティングが実行されます。おお、現代のハードウェアは素晴らしいですね!
コード結合の非常に単純なテストを提案します。
コードの断片 A は、正確性を維持するために断片 A の変更を強制する可能性のある変更が断片 B に存在する場合、コードの断片 B に密結合されます。
部分 A への変更を必要とする可能性のある変更が部分 B にない場合、コードの部分 A はコードの部分 B に密結合されていません。
これは、コードの断片間にどの程度の結合があるかを確認するのに役立ちます。その理由については、このブログ投稿を参照してください。
ここにいくつかの長い答えがあります。原理は非常に単純ですが。ウィキペディアから冒頭陳述を提出します:
「疎結合とは、ある種の交換関係を持つ 2 つ以上のシステムまたは組織間の回復力のある関係を表します。
トランザクションのそれぞれの側で要件が明確になり、相手側についてはほとんど仮定がありません。」
「疎結合」の一般的な概念について詳しく読むことができます。
簡単に言えば、これは 2 つのクラス間の関係の記述であり、各クラスは他のクラスについてほとんど知らず、各クラスは、他のクラスが存在するかどうかに関係なく、他のクラスの特定の実装に依存することなく、問題なく動作し続ける可能性があります。クラス。
一般に、疎結合は、同じワークロードで互いに独立して作業する 2 つのアクターです。したがって、同じバックエンド データベースを使用する 2 つの Web サーバーがある場合、これらの Web サーバーは疎結合であると言えます。密結合は、1 つの Web サーバーに 2 つのプロセッサを搭載することによって例示されます。これらのプロセッサは密結合されています。
それが多少役立つことを願っています。