Che cos’è un “accoppiamento lento?” Fornire esempi

Non riesco a capire il concetto di “accoppiamento lento”. Suppongo che non sia di aiuto il fatto che la parola “loose” di solito abbia una connotazione negativa, quindi dimentico sempre che l’accoppiamento libero è una buona cosa.

Qualcuno può mostrare qualche codice “prima” e “dopo” (o pseudocodice) che illustra questo concetto?

Considera una semplice applicazione del carrello degli acquisti che utilizza una class CartContents per tenere traccia degli articoli nel carrello e una class di ordine per l’elaborazione di un acquisto. L’ordine deve determinare il valore totale dei contenuti nel carrello, potrebbe farlo in questo modo:

Esempio strettamente accoppiato:

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; } } 

Si noti come il metodo OrderTotal (e quindi la class Order) dipende dai dettagli di implementazione delle classi CartContents e CartEntry. Se dovessimo provare a cambiare questa logica per consentire sconti, dovremmo probabilmente cambiare tutte e 3 le classi. Inoltre, se cambiamo l'uso di una raccolta di elenchi per tenere traccia degli articoli, dovremmo cambiare anche la class dell'ordine.

Ora ecco un modo leggermente migliore di fare la stessa cosa:

Esempio meno abbinato:

 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); } } 

La logica specifica per l'implementazione dell'elemento pubblicitario del carrello o della raccolta del carrello o dell'ordine è limitata solo a quella class. Quindi potremmo cambiare l'implementazione di una di queste classi senza dover cambiare le altre classi. Potremmo eliminare ulteriormente questo disaccoppiamento migliorando il design, introducendo interfacce, ecc., Ma penso che tu capisca il punto.

Molti prodotti integrati (soprattutto Apple) come iPod , iPad sono un buon esempio di accoppiamento stretto: una volta che la batteria si spegne, potresti anche comprare un nuovo dispositivo perché la batteria è saldata e non si allenta, rendendo così la sostituzione molto costoso. Un lettore liberamente accoppiato permetterebbe di cambiare la batteria senza sforzo.

Lo stesso vale per lo sviluppo del software: in genere (molto) è meglio avere un codice liberamente accoppiato per facilitare l’estensione e la sostituzione (e rendere le singole parti più facili da capire). Ma, molto raramente, in circostanze particolari l’accoppiamento stretto può essere vantaggioso perché la stretta integrazione di più moduli consente una migliore ottimizzazione.

Userò Java come esempio. Diciamo che abbiamo una class simile a questa:

 public class ABC { public void doDiskAccess() {...} } 

Quando chiamo la class, dovrò fare qualcosa del genere:

 ABC abc = new ABC(); abc. doDiskAccess(); 

Fin qui tutto bene. Ora diciamo che ho un’altra class simile a questa:

 public class XYZ { public void doNetworkAccess() {...} } 

Sembra esattamente lo stesso di ABC, ma diciamo che funziona sulla rete anziché su disco. Quindi ora scriviamo un programma come questo:

 if(config.isNetwork()) new XYZ().doNetworkAccess(); else new ABC().doDiskAccess(); 

Funziona, ma è un po ‘ingombrante. Potrei semplificarlo con un’interfaccia come questa:

 public interface Runnable { public void run(); } public class ABC implements Runnable { public void run() {...} } public class XYZ implements Runnable { public void run() {...} } 

Ora il mio codice può assomigliare a questo:

 Runnable obj = config.isNetwork() ? new XYZ() : new ABC(); obj.run(); 

Vedi quanto è più pulito e semplice da capire? Abbiamo appena compreso il primo principio di base di accoppiamento libero: l’astrazione. La chiave da qui è garantire che ABC e XYZ non dipendano da alcun metodo o variabile delle classi che li chiamano. Ciò consente a ABC e XYZ di essere API completamente indipendenti. O in altre parole, sono “disaccoppiati” o “liberamente accoppiati” dalle classi genitore.

Ma cosa succede se abbiamo bisogno di comunicazione tra i due? Bene, allora possiamo usare ulteriori astrazioni come un modello di eventi per garantire che il codice genitore non abbia mai bisogno di accoppiarsi con le API che hai creato.

Spiacente, ma “accoppiamento lento” non è un problema di codifica, è un problema di progettazione. Il termine “accoppiamento lento” è intimamente correlato allo stato desiderabile di “alta coesione”, essendo opposti ma complementari.

L’accoppiamento lento significa semplicemente che i singoli elementi di progettazione devono essere costruiti in modo da ridurre la quantità di informazioni non necessarie di cui hanno bisogno per conoscere altri elementi di progettazione.

L’alta coesione è un po ‘come un “accoppiamento stretto”, ma un’elevata coesione è uno stato in cui gli elementi di design che devono realmente conoscersi l’un l’altro sono progettati in modo che lavorino insieme in modo pulito ed elegante.

Il punto è che alcuni elementi di design dovrebbero conoscere i dettagli di altri elementi di design, quindi dovrebbero essere progettati in quel modo e non in modo accidentale. Altri elementi di design non dovrebbero conoscere dettagli su altri elementi di design, quindi dovrebbero essere progettati in questo modo, in modo mirato, invece che a caso.

L’attuazione di questo è lasciato come un esercizio per il lettore :).

Il codice strettamente accoppiato si basa su un’implementazione concreta. Se ho bisogno di un elenco di stringhe nel mio codice e lo dichiaro in questo modo (in Java)

 ArrayList myList = new ArrayList(); 

quindi sono dipendente dall’implementazione di ArrayList.

Se voglio cambiarlo in codice liberamente accoppiato, creo il mio riferimento come un tipo di interfaccia (o di altro tipo astratto).

 List myList = new ArrayList(); 

Questo mi impedisce di chiamare qualsiasi metodo su myList che sia specifico per l’implementazione ArrayList. Sono limitato ai soli metodi definiti nell’interfaccia Elenco. Se deciderò in seguito che ho davvero bisogno di una LinkedList, ho solo bisogno di cambiare il mio codice in un posto, dove ho creato la nuova lista, e non in 100 posti dove ho fatto chiamate ai metodi ArrayList.

Ovviamente, è ansible creare un’istanza di ArrayList utilizzando la prima dichiarazione e trattenersi dal non utilizzare alcun metodo che non fa parte dell’interfaccia di List, ma l’uso della seconda dichiarazione rende il compilatore fedele.

Puoi pensare a un accoppiamento (stretto o allentato) come se fosse letteralmente la quantità di sforzo che ti porterebbe a separare una particolare class dalla sua dipendenza da un’altra class. Ad esempio, se tutti i metodi della class avessero un piccolo blocco alla fine in cui hai effettuato una chiamata a Log4Net per registrare qualcosa, allora la tua class sarebbe strettamente connessa a Log4Net. Se invece la tua class conteneva un metodo privato chiamato LogSomething, che era l’unico posto che chiamava il componente Log4Net (e gli altri metodi tutti chiamati LogSomething), allora si direbbe che la propria class fosse accoppiata liberamente a Log4Net (perché non ci sarebbe voluto molto sforzo per estrarre Log4Net e sostituirlo con qualcos’altro).

Il grado di differenza tra le risposte qui mostra perché sarebbe un concetto difficile da cogliere, ma per dirla semplicemente come posso descriverlo:

Per farmi sapere che se ti lancio una palla, allora puoi prenderla, non ho davvero bisogno di sapere quanti anni hai. Non ho bisogno di sapere cosa hai mangiato a colazione, e non mi interessa davvero chi fosse la tua prima cotta. Tutto quello che devo sapere è che puoi prendere. Se lo so, allora non mi interessa se il suo io sto lanciando una palla a te o tuo fratello.

Con linguaggi non dinamici come c # o Java ecc, lo facciamo tramite Interfacce. Quindi diciamo che abbiamo la seguente interfaccia:

 public ICatcher { public void Catch(); } 

E ora diciamo che abbiamo le seguenti classi:

 public CatcherA : ICatcher { public void Catch() { console.writeline("You Caught it"); } } public CatcherB : ICatcher { public void Catch() { console.writeline("Your brother Caught it"); } } 

Ora sia CatcherA che CatcherB implementano il metodo Catch, quindi il servizio che richiede un Catcher può utilizzare uno di questi e non gliene frega niente di quale sia. Quindi un servizio strettamente accoppiato potrebbe direttamente instanciare un ie catturato

 public CatchService { private CatcherA catcher = new CatcherA(); public void CatchService() { catcher.Catch(); } } 

Quindi CatchService può fare esattamente ciò che ha deciso di fare, ma usa CatcherA e userà sempre CatcherA. Il suo codice è rigido, quindi resta lì finché qualcuno non arriva e lo rifiuta.

Ora lasciamo prendere un’altra opzione, chiamata iniezione di dipendenza:

 public CatchService { private ICatcher catcher; public void CatchService(ICatcher catcher) { this.catcher = catcher; catcher.Catch(); } } 

Quindi il calss che instrada CatchService può fare quanto segue:

 CatchService catchService = new CatchService(new CatcherA()); 

o

 CatchService catchService = new CatchService(new CatcherB()); 

Ciò significa che il servizio Catch non è strettamente accoppiato a CatcherA o CatcherB.

Ci sono molte altre stratergie per servizi di accoppiamento libero come questo, come l’uso di un framework IoC, ecc.

Definizione

Essenzialmente, l’accoppiamento è quanto un dato object o insieme di oggetti si basa su un altro object o su un altro insieme di oggetti per svolgere il suo compito.

Alto accoppiamento

Pensa a una macchina. Per avviare il motore, è necessario inserire una chiave nell’accensione, girare, la benzina deve essere presente, deve verificarsi una scintilla, i pistoni devono sparare e il motore deve prendere vita. Si potrebbe dire che un motore di un’auto è altamente accoppiato a molti altri oggetti. Questo è alto accoppiamento, ma non è davvero una brutta cosa.

Accoppiamento lasco

Pensa a un controllo utente per una pagina Web che è responsabile per consentire agli utenti di pubblicare, modificare e visualizzare alcuni tipi di informazioni. Il singolo controllo può essere utilizzato per consentire a un utente di pubblicare una nuova informazione o modificare una nuova informazione. Il controllo dovrebbe essere in grado di essere condiviso tra due diversi percorsi: nuovi e modificati. Se il controllo è scritto in modo tale da richiedere un qualche tipo di dati dalle pagine che lo contengono, allora si potrebbe dire che è troppo abbinato. Il controllo non dovrebbe aver bisogno di nulla dalla sua pagina del contenitore.

È un concetto piuttosto generale, quindi gli esempi di codice non daranno l’intera immagine.

Un ragazzo qui al lavoro mi ha detto, “i pattern sono come i frattali, puoi vederli quando si avvicina lo zoom e quando si esegue lo zoom verso il livello di architettura”.

Leggendo la breve pagina di Wikipedia puoi darti un’idea di questa genericità:

http://en.wikipedia.org/wiki/Loose_coupling

Per quanto riguarda un esempio di codice specifico …

Ecco un accoppiamento non valido con cui ho lavorato di recente, dalla roba Microsoft.Practices.CompositeUI.

  [ServiceDependency] public ICustomizableGridService CustomizableGridService { protected get { return _customizableGridService; } set { _customizableGridService = value; } } 

Questo codice dichiara che questa class ha una dipendenza da un CustomizableGridService. Invece di riferirsi direttamente all’esatta implementazione del servizio, afferma semplicemente che richiede un’implementazione SOME di quel servizio. Quindi in fase di esecuzione, il sistema risolve tale dipendenza.

Se questo non è chiaro, puoi leggere una spiegazione più dettagliata qui:

http://en.wikipedia.org/wiki/Dependency_injection

Immagina che ABCCustomizableGridService sia l’impementazione che intendo colbind qui.

Se scelgo di farlo, posso estrapolarlo e sostituirlo con XYZCustomizableGridService o StubCustomizableGridService senza alcun cambiamento alla class con questa dipendenza.

Se avessi fatto riferimento direttamente a ABCCustomizableGridService, allora avrei bisogno di apportare modifiche a quel / i / i riferimento / i per poterlo scambiare in un’altra implementazione di servizio.

L’accoppiamento ha a che fare con le dipendenze tra i sistemi, che potrebbero essere moduli di codice (funzioni, file o classi), strumenti in una pipeline, processi client-server e così via. Meno sono generali le dipendenze, più diventano “strettamente accoppiate”, dal momento che cambiare un sistema richiede cambiare gli altri sistemi che si basano su di esso. La situazione ideale è un “accoppiamento libero” in cui un sistema può essere modificato e i sistemi che dipendono da esso continueranno a funzionare senza modifiche.

Il modo generale per ottenere un accoppiamento lento è attraverso interfacce ben definite. Se l’interazione tra due sistemi è ben definita e rispettata su entrambi i lati, diventa più semplice modificare un sistema assicurandosi che le convenzioni non vengano interrotte. Nella pratica si verifica in pratica che non è stata stabilita un’interfaccia ben definita, risultando in un design sciatto e un accoppiamento stretto.

Qualche esempio:

  • L’applicazione dipende da una libreria. Sotto stretto accoppiamento, l’app si rompe nelle versioni più recenti della lib. Google per “DLL Hell”.

  • L’app client legge i dati da un server. Sotto stretto accoppiamento, le modifiche al server richiedono correzioni sul lato client.

  • Due classi interagiscono in una gerarchia orientata agli oggetti. In condizioni di accoppiamento stretto, le modifiche a una class richiedono che l’altra class venga aggiornata per corrispondere.

  • Più strumenti da riga di comando comunicano in una pipe. Se sono strettamente accoppiati, le modifiche alla versione di uno strumento da riga di comando causano errori negli strumenti che leggono il suo output.

Due componenti sono fortemente accoppiati quando dipendono dall’implementazione concreta l’uno dell’altro.

Supponiamo di avere questo codice da qualche parte in un metodo della mia class:

 this.some_object = new SomeObject(); 

Ora la mia class dipende da SomeObject e sono altamente accoppiati. D’altra parte, diciamo che ho un metodo InjectSomeObject:

 void InjectSomeObject(ISomeObject so) { // note we require an interface, not concrete implementation this.some_object = so; } 

Quindi il primo esempio può usare solo SomeObject iniettato. Questo è utile durante i test. Con il normale funzionamento è ansible utilizzare classi pesanti, che utilizzano database, utilizzare la rete, ecc. Mentre per i test si passa a un’implementazione leggera e fittizia. Con il codice strettamente accoppiato non puoi farlo.

È ansible semplificare alcune parti di questo lavoro utilizzando i contenitori di iniezione delle dipendenze. Puoi leggere ulteriori informazioni su DI su Wikipedia: http://en.wikipedia.org/wiki/Dependency_injection .

A volte è facile prendere questo troppo lontano. Ad un certo punto devi rendere le cose concrete, o il tuo programma sarà meno leggibile e comprensibile. Quindi usa queste tecniche principalmente al limite dei componenti e sai cosa stai facendo. Assicurati di sfruttare l’accoppiamento libero. Se no, probabilmente non ne hai bisogno in quel posto. DI potrebbe rendere il tuo programma più complesso. Assicurati di fare un buon compromesso. In altre parole, mantenere un buon equilibrio. Come sempre quando si progettano i sistemi. In bocca al lupo!

Considera un’applicazione per Windows con FormA e FormB. FormA è la forma principale e visualizza FormB. Immagina che FormB debba passare i dati al suo genitore.

Se hai fatto questo:

 class FormA { FormB fb = new FormB( this ); ... fb.Show(); } class FormB { FormA parent; public FormB( FormA parent ) { this.parent = parent; } } 

FormB è strettamente accoppiato a FormA. FormB non può avere altro genitore di quello di tipo FormA.

Se, invece, FormB ha pubblicato un evento e si è iscritto FormA a quell’evento, FormB potrebbe inviare nuovamente i dati attraverso quell’evento a qualsiasi sottoscrittore di quell’evento. In questo caso, quindi, FormB non sa nemmeno che sta parlando al genitore; attraverso l’accoppiamento libero l’evento prevede che si tratta semplicemente di parlare con gli abbonati. Qualsiasi tipo può ora essere padre di FormA.

rp

Nell’informatica c’è un altro significato per “accoppiamento lento” che nessun altro ha pubblicato qui, quindi … Ecco qui – spero che mi darai dei voti in modo che questo non si perda in fondo al mucchio! Sicuramente l’argomento della mia risposta appartiene a qualsiasi risposta esauriente alla domanda … A titolo di esempio:

Il termine “Loose Coupling” è entrato per la prima volta in informatica come termine usato come aggettivo per l’architettura della CPU in una configurazione multi-CPU. Il suo termine equivalente è “accoppiamento stretto”. Loose Coupling è quando le CPU non condividono molte risorse in comune e Tight Coupling è quando lo fanno.

Il termine “sistema” può essere fonte di confusione qui quindi si prega di analizzare attentamente la situazione.

Di solito, ma non sempre, più CPU in una configurazione hardware in cui esse esistono all’interno di un sistema (come nelle singole caselle “PC”) sarebbero strettamente accoppiate. Ad eccezione di alcuni sistemi ad altissime prestazioni che hanno sottosistemi che condividono effettivamente la memoria principale attraverso “sistemi”, tutti i sistemi divisibili sono accoppiati liberamente.

I termini Strettamente accoppiati e liberamente accoppiati sono stati introdotti prima che fossero inventate CPU multi-thread e multi-core, quindi questi termini potrebbero richiedere alcuni compagni per articolare completamente la situazione oggi. E, in effetti, oggi si può benissimo avere un sistema che comprende entrambi i tipi in un sistema globale. Per quanto riguarda i sistemi software attuali, ci sono due architetture comuni, una per ciascuna varietà, abbastanza comuni da dover essere familiari.

Innanzitutto, poiché era la domanda, alcuni esempi di sistemi liberamente accoppiati:

  • VaxClusters
  • Cluster Linux

Al contrario, alcuni esempi strettamente accoppiati:

  • Sistemi operativi Semetrical-Multi-Processing (SMP) – ad es. Fedora 9
  • CPU multi-thread
  • CPU multi-core

Nell’informatica di oggi, esempi di entrambi operanti in un singolo sistema generale non sono rari. Ad esempio, prendi le moderne CPU Pentium dual o quad core con Fedora 9 – questi sono sistemi di elaborazione strettamente accoppiati. Quindi, combina diversi di essi in un Cluster Linux liberamente accoppiato e ora hai in corso l’elaborazione sia accoppiato che strettamente! Oh, l’hardware moderno non è meraviglioso!

Accoppiamento si riferisce a quanto strettamente diverse classi sono collegate tra loro. Le classi strettamente accoppiate contengono un elevato numero di interazioni e dipendenze.

Le classi vagamente accoppiate sono l’opposto in quanto le loro dipendenze l’una sull’altra sono ridotte al minimo e invece si affidano alle interfacce pubbliche ben definite l’una dall’altra.

I lego, i giocattoli che lo SNAP sono considerati insieme sono strettamente accoppiati perché puoi semplicemente agganciare i pezzi e build qualsiasi sistema tu voglia. Tuttavia, un puzzle ha pezzi che sono strettamente accoppiati. Non puoi prendere un pezzo da un jigsaw puzzle (sistema) e farlo scattare in un puzzle diverso, perché il sistema (puzzle) dipende molto dai pezzi molto specifici che sono stati costruiti specifici per quel particolare “progetto”. I lego sono costruiti in modo più generico in modo che possano essere utilizzati nella Lego House o nel mio Lego Alien Man.

Riferimento: https://megocode3.wordpress.com/2008/02/14/coupling-and-cohesion/

In un linguaggio semplice, in modo approssimativo, significa che non dipende da altri eventi. Esegue indipendentemente.

Alcune lunghe risposte qui. Il principio è molto semplice però. Invio la dichiarazione di apertura di wikipedia :

“L’accoppiamento lento descrive una relazione resiliente tra due o più sistemi o organizzazioni con qualche tipo di relazione di scambio.

Ogni fine della transazione rende esplicite le sue esigenze e fa alcune supposizioni sull’altro capo. ”

Propongo un test di accoppiamento del codice molto semplice:

  1. Il pezzo A del codice è strettamente collegato al pezzo B del codice se esiste una ansible modifica al pezzo B che imporrebbe modifiche nel pezzo A per mantenere la correttezza.

  2. Il pezzo A del codice non è strettamente accoppiato al pezzo B del codice se non è ansible apportare modifiche al pezzo B che renderebbe necessaria una modifica al pezzo A.

Questo ti aiuterà a verificare quanto accoppiamento c’è tra i pezzi del tuo codice. per ragionamenti su questo vedi questo post sul blog: http://marekdec.wordpress.com/2012/11/14/loose-coupling-tight-coupling-decoupling-what-is-that-all-about/

Quando crei un object di una class usando una new parola chiave in qualche altra class, stai effettivamente facendo un accoppiamento stretto (ctriggers pratica), invece dovresti usare un accoppiamento libero che è una buona pratica

— — 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 } } 

Spiegazione:

Nell’esempio sopra, abbiamo due classi A e B

Interfacce di class B, ovvero InterfaceClass.

InterfaceClass definisce un contratto per la class B come InterfaceClass hanno metodi astratti di class B che possono essere accessibili da qualsiasi altra class ad esempio A.

Nella class A abbiamo un metodo di visualizzazione che può escludere l’object di class che implementa InterfaceClass (nel nostro caso è di class B). E su quel metodo object della class A sta chiamando display () e getVar () della class B

In MainClass abbiamo creato l’object di Classe A e B. E chiamando il metodo di visualizzazione di A passando l’object di class B cioè objb. Il metodo di visualizzazione di A sarà chiamato con object di class B.

Ora parliamo di accoppiamento libero. Supponiamo che in futuro si debba cambiare il nome della class B in ABC, quindi non è necessario cambiarne il nome nel metodo di visualizzazione della class B, basta creare l’object della nuova (class ABC) e passarlo al metodo di visualizzazione in MailClass. Non devi cambiare nulla in Classe A

ref: http://p3lang.com/2013/06/loose-coupling-example-using-interface/

Puoi leggere di più sul concetto generico di “accoppiamento libero” .

In breve, è una descrizione di una relazione tra due classi, in cui ogni class conosce il minimo sull’altro e ogni class potrebbe potenzialmente continuare a funzionare perfettamente se l’altro è presente o meno e senza dipendere dalla particolare implementazione dell’altro class.

Forse la migliore metafora è il matrimonio.

Quando non sei sposato, sei semplicemente accoppiato.

Puoi lasciare il tuo partner più facilmente.

Quando sei “sposato”, sei strettamente unito.

Per esempio in alcuni paesi devi pagare degli alimenti quando lasci il tuo partner.

L’accoppiamento lento, in generale, è di 2 attori che lavorano indipendentemente l’uno dall’altro sullo stesso carico di lavoro. Quindi, se avessi 2 server web che usano lo stesso database di back-end, allora diresti che quei server web sono accoppiati liberamente. Un accoppiamento stretto sarebbe esemplificato dall’avere 2 processori su un server web … quei processori sono strettamente accoppiati.

Spero che sia alquanto utile.