「ルーズカップリング」の概念を理解できないようです。「緩い」という言葉が通常否定的な意味を持つことは役に立たないと思うので、疎結合が良いことをいつも忘れますことであることます。
誰かがこの概念を説明する「前」と「後」のコード(または疑似コード)を見せてくれませんか?
「ルーズカップリング」の概念を理解できないようです。「緩い」という言葉が通常否定的な意味を持つことは役に立たないと思うので、疎結合が良いことをいつも忘れますことであることます。
誰かがこの概念を説明する「前」と「後」のコード(または疑似コード)を見せてくれませんか?
回答:
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つのクラスすべてを変更する必要があります。また、Listコレクションを使用してアイテムを追跡するように変更した場合は、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);
}
}
カートのラインアイテムまたはカートのコレクションまたは注文の実装に固有のロジックは、そのクラスのみに制限されています。したがって、他のクラスを変更せずに、これらのクラスの実装を変更できます。デザインを改善したり、インターフェースを導入したりすることで、このデカップリングをさらに進めることができますが、要点は理解できると思います。
Order
がの知識を持っている理由はないと思いますCart
。ライブショッピングとファクトレーションは、2つの異なるコンテキストです。クライアントが支払いの準備ができたら、CartEntriesをにとって意味のあるものに変換する責任のあるプロセスを持つことができますOrder
。このようにして、Order
クラスはインスタンス化されずに使用されCart
ます。
CartEntry
およびの「実装」の詳細を非表示にしても意味がありませんCartContents
。これらは完全に明確な意味を持っているので、それらは単に死んだデータとしてモデル化されるべきです。データベースの用語では、ここで必要なのはテーブルだけですCREATE TABLE entry (price INTEGER, quantity INTEGER)
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と結合する必要がないようにします。
DiskAccessor
andのような名前に変更された場合、これはより読みやすくなりNetworkAccessor
ますか?私はJavaを
申し訳ありませんが、「疎結合」はコーディングの問題ではなく、設計の問題です。「疎結合」という用語は、「高い凝集力」の望ましい状態と密接に関連しており、反対ですが相補的です。
疎結合とは、個々の設計要素を構築して、他の設計要素について知る必要のある不要な情報の量を減らすことを意味します。
高い凝集力は一種の「密結合」のようなものですが、高い凝集力は、お互いについて本当に知る必要のある設計要素がクリーンでエレガントに連携するように設計されている状態です。
重要なのは、一部の設計要素は他の設計要素の詳細を知っている必要があるため、誤ってではなく、そのように設計する必要があるということです。他の設計要素は他の設計要素の詳細を知らないはずなので、ランダムにではなく、そのように意図的に設計する必要があります。
これを実装することは、読者の練習問題として残します:)。
密結合コードは、具体的な実装に依存しています。コードに文字列のリストが必要で、これを次のように宣言した場合(Javaの場合)
ArrayList<String> myList = new ArrayList<String>();
その後、私はArrayListの実装に依存しています。
それを疎結合コードに変更したい場合は、参照をインターフェース(または他の抽象)タイプにします。
List<String> myList = new ArrayList<String>();
これにより、メソッドを呼び出せなくなりますmyList
それは、ArrayListの実装に固有です。Listインターフェースで定義されているメソッドのみに制限されています。後でLinkedListが本当に必要だと判断した場合は、新しいリストを作成した1か所でコードを変更するだけでよく、ArrayListメソッドを呼び出した100か所では変更しません。
もちろん、あなたができる最初の宣言を使用しての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を使用し、常に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フレームワークの使用など、このような疎結合サービスには他にもいくつかの戦略があります。
(タイトまたはルーズ)カップリングは、特定のクラスを別のクラスへの依存から分離するために必要な文字通りの労力であると考えることができます。たとえば、クラスのすべてのメソッドの最後に、何かをログに記録するためにLog4Netを呼び出す小さなブロックがあった場合、クラスはLog4Netに密結合されていると言えます。クラスに、Log4Netコンポーネントを呼び出す唯一の場所であるLogSomethingというプライベートメソッドが含まれていた場合(および、他のすべてのメソッドが代わりにLogSomethingを呼び出した場合)、クラスはLog4Netに疎結合されていると言えます(それほど多くの時間がかかるわけではないためです)。 Log4Netを引き出して別のものに置き換えるための努力)。
64BitBob
は自分自身の答えが好きです。
基本的に、結合とは、特定のオブジェクトまたはオブジェクトのセットが、そのタスクを実行するために、別のオブジェクトまたは別のオブジェクトのセットにどれだけ依存しているかを示します。
車について考えてください。エンジンを始動するには、キーを点火装置に挿入し、回転させ、ガソリンを供給し、火花を発生させ、ピストンを始動させ、エンジンを始動させる必要があります。車のエンジンは他のいくつかのオブジェクトと高度に結合していると言えます。これは高い結合ですが、それは実際に悪いことではありません。
ユーザーが特定の種類の情報を投稿、編集、表示できるようにするWebページのユーザーコントロールについて考えてみます。単一のコントロールを使用して、ユーザーが新しい情報を投稿したり、新しい情報を編集したりできます。コントロールは、2つの異なるパス(新規および編集)間で共有できる必要があります。コントロールが、そのコントロールを含むページからのある種のデータを必要とするような方法で記述されている場合は、結合が強すぎると言えます。コントロールはそのコンテナーページから何も必要ありません。
これはかなり一般的な概念であるため、コード例では全体像がわかりません。
「パターンはフラクタルのようなもので、実際にズームインすると、アーキテクチャレベルにズームアウトすると、パターンを見ることができます。」
ウィキペディアの簡単なページを読むと、この一般性を感じることができます。
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を直接参照していた場合、別のサービス実装でスワップするために、その参照を変更する必要があります。
結合は、システム間の依存関係に関係します。システム間の依存関係には、コードのモジュール(関数、ファイル、またはクラス)、パイプラインのツール、サーバークライアントプロセスなどがあります。1つのシステムを変更すると、それに依存する他のシステムを変更する必要があるため、依存関係が一般的でないほど、それらはより「密結合」になります。理想的な状況は、1つのシステムを変更でき、それに依存するシステムが変更なしで機能し続ける「疎結合」です。
疎結合を実現する一般的な方法は、明確に定義されたインターフェースを使用することです。2つのシステム間の相互作用が明確に定義され、両側で守られている場合は、規則に違反しないようにしながら、1つのシステムを変更することが容易になります。よく定義されたインターフェースが確立されていないことが実際に発生し、その結果、デザインがずさんになり、結合が密になります。
いくつかの例:
アプリケーションはライブラリに依存します。密結合の下では、アプリは新しいバージョンのlibで中断します。「DLL Hell」のGoogle。
クライアントアプリはサーバーからデータを読み取ります。密結合では、サーバーへの変更にはクライアント側での修正が必要です。
2つのクラスがオブジェクト指向階層で相互作用します。密結合では、1つのクラスを変更するには、他のクラスを一致するように更新する必要があります。
複数のコマンドラインツールがパイプで通信します。それらが密に結合されている場合、1つのコマンドラインツールのバージョンを変更すると、その出力を読み取るツールでエラーが発生します。
結合とは、さまざまなクラスが互いにどの程度密接に接続されているかを指します。密結合クラスには、多数の相互作用と依存関係が含まれています。
疎結合クラスは、相互の依存関係が最小限に保たれ、代わりに明確に定義された相互のパブリックインターフェイスに依存するという点で、反対です。
レゴ、SNAPが一緒におもちゃを一緒にスナップし、必要なシステムを構築できるため、疎結合と見なされます。ただし、ジグソーパズルには、密結合のピースがあります。1つのジグソーパズル(システム)からピースを取り出して別のパズルにスナップすることはできません。システム(パズル)は、特定の「デザイン」に固有に構築された非常に具体的なピースに大きく依存しているためです。レゴはより一般的な方法で構築されているため、レゴハウスや私のレゴエイリアンマンで使用できます。
リファレンス:https : //megocode3.wordpress.com/2008/02/14/coupling-and-cohesion/
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には、フォームAの親以外の親はありません。
一方、FormBにイベントを発行させ、FormAにそのイベントをサブスクライブさせると、FormBはそのイベントを介して、そのイベントが持つサブスクライバーにデータをプッシュバックできます。この場合、FormBは親とのやり取りについても知りません。疎結合により、イベントは単にサブスクライバーと話しているだけです。これで、任意のタイプをFormAの親にすることができます。
rp
コンピュータサイエンスでは、ここで他の誰も投稿していない「疎結合」の別の意味があるので、ここに行きます-うまくいけば、あなたが私に投票して、ヒープの下部で失われないようにしてください!確かに私の答えの主題は質問への包括的な答えに属しています...ウィットするには:
「ルーズカップリング」という用語は、マルチCPU構成のCPUアーキテクチャに関する形容詞として使用される用語として最初にコンピューティングに入りました。対応する用語は「密結合」です。疎結合は、CPUが多くのリソースを共有しない場合であり、密結合はそれらを共有する場合です。
「システム」という用語はここでは混乱する可能性があるので、状況を注意深く解析してください。
常にではありませんが、通常、1つのシステム内に存在するハードウェア構成の複数のCPU(個々の「PC」ボックスなど)は、密結合されます。「システム」間で実際にメインメモリを共有するサブシステムを持ついくつかの超高性能システムを除いて、すべての分割可能なシステムは疎結合です。
マルチスレッドおよびマルチコアCPUが発明される前に、密結合および疎結合という用語が導入されたため、これらの用語は、今日の状況を完全に説明するためにいくつかの仲間が必要になる場合があります。そして実際、今日では、1つのシステム全体で両方のタイプを包括するシステムが非常によくあるでしょう。現在のソフトウェアシステムに関しては、2つの一般的なアーキテクチャがあり、それぞれに1つずつありますが、これらはよく知られているはずです。
まず、それが問題であったため、疎結合システムのいくつかの例:
対照的に、いくつかの密結合の例:
今日のコンピューティングでは、両方が単一のシステム全体で動作する例は珍しくありません。たとえば、Fedora 9を実行する最新のPentiumデュアルまたはクアッドコアCPUを考えてみます。これらは密結合のコンピューティングシステムです。次に、それらのいくつかを疎結合Linuxクラスターに結合すると、疎結合と密結合の両方のコンピューティングが実行されます!ああ、現代のハードウェアは素晴らしいものではありません!
単純な言語では、疎結合とは、発生する他のイベントに依存しないことを意味します。独立して実行されます。
コード結合の非常に簡単なテストを提案します。
正確さを保つためにピースAを強制的に変更する可能性のあるピースBへの変更が存在する場合、ピースAのコードはピースBのコードに密結合されます。
ピースAへの変更を必要とする可能性のあるピースBへの変更がない場合、ピースAのコードはピースBのコードと緊密に結合されていません。
これは、コードの断片間にどの程度の結合があるかを確認するのに役立ちます。その理由については、次のブログ投稿を参照してください。http://marekdec.wordpress.com/2012/11/14/loose-coupling-tight-coupling-decoupling-what-is-that-all-about/
を使用してクラスのオブジェクトを作成するとき 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クラスのコントラクトを定義します。InterfaceClassには、Aなどの他のクラスからアクセスできる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-coupling-example-using-interface/
一般に、疎結合は、同じワークロードで互いに独立して動作する2つのアクターです。したがって、同じバックエンドデータベースを使用する2つのWebサーバーがある場合、これらのWebサーバーは疎結合されていると言えます。密結合の例として、1つのWebサーバーに2つのプロセッサを搭載することが挙げられます。これらのプロセッサは密結合されています。
少しお役に立てば幸いです。