web-dev-qa-db-fra.com

Qu'est-ce qu'un "couplage lâche?" S'il vous plaît fournir des exemples

Je n'arrive pas à comprendre le concept de "couplage lâche". Je suppose que cela n'aide pas que le mot "lâche" ait généralement une connotation négative, donc j'oublie toujours que le couplage lâche est une bonne chose .

Quelqu'un voudra-t-il s'il vous plaît montrer du code "avant" et "après" (ou pseudocode) illustrant ce concept?

166
trebormf

Envisagez une application de panier simple qui utilise une classe CartContents pour garder une trace des articles du panier et une classe Order pour le traitement d'un achat. La commande doit déterminer la valeur totale du contenu du panier, elle pourrait le faire de la manière suivante:

Exemple étroitement couplé:

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

Notez que la méthode OrderTotal (et donc la classe Order) dépend des détails d'implémentation des classes CartContents et CartEntry. Si nous essayions de changer cette logique pour permettre des remises, nous devrions probablement changer les 3 classes. De même, si nous utilisons une collection List pour garder une trace des éléments, nous devrons également modifier la classe Order.

Maintenant, voici une façon légèrement meilleure de faire la même chose:

Exemple moins couplé:

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 logique propre à la mise en œuvre de l'élément de campagne "panier" ou de la collection du panier ou de la commande est limitée à cette classe. Nous pourrions donc changer l'implémentation de l'une de ces classes sans avoir à changer les autres classes. Nous pourrions pousser plus loin ce découplage en améliorant la conception, en introduisant des interfaces, etc., mais je pense que vous voyez le point.

168
Wedge

De nombreux produits intégrés (en particulier Apple) tels que iPods, iPads sont un bon exemple de couplage étroit: une fois la batterie épuisée, vous pourriez aussi bien acheter un nouvel appareil, car la batterie est soudé fixe et ne se détache pas, rendant son remplacement très coûteux. Un lecteur faiblement couplé permettrait de changer la batterie sans effort.

Le même pour le développement de logiciels: il est généralement (bien) préférable d’avoir un code faiblement couplé pour faciliter l’extension et le remplacement (et pour faciliter la compréhension des composants individuels). Mais, très rarement, dans des circonstances spéciales, un couplage étroit peut être avantageux car l'intégration étroite de plusieurs modules permet une meilleure optimisation.

152
Konrad Rudolph

Je vais utiliser Java à titre d'exemple. Supposons que nous ayons une classe qui ressemble à ceci:

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

Lorsque j'appelle la classe, je dois faire quelque chose comme ceci:

ABC abc = new ABC();

abc. doDiskAccess();

Jusqu'ici tout va bien. Maintenant, disons que j'ai un autre cours qui ressemble à ceci:

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

Cela ressemble exactement à ABC, mais supposons que cela fonctionne sur le réseau plutôt que sur le disque. Alors maintenant, écrivons un programme comme celui-ci:

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

Cela fonctionne, mais c'est un peu lourd. Je pourrais simplifier cela avec une interface comme celle-ci:

public interface Runnable
{
    public void run();
}

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

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

Maintenant, mon code peut ressembler à ceci:

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

obj.run();

Voyez-vous à quel point c'est plus propre et plus simple à comprendre? Nous venons de comprendre le premier principe de base du couplage lâche: l'abstraction. La clé ici est de s'assurer que ABC et XYZ ne dépendent pas de méthodes ou de variables des classes qui les appellent. Cela permet à ABC et XYZ d'être des API complètement indépendantes. Ou en d'autres termes, ils sont "découplés" ou "faiblement couplés" des classes parentes.

Mais que faire si nous avons besoin de communication entre les deux? Dans ce cas, nous pouvons utiliser d'autres abstractions, telles que modèle d'événement , pour garantir que le code parent n'a jamais besoin d'être couplé aux API que vous avez créées.

53
64BitBob

Désolé, mais le "couplage faible" n'est pas un problème de codage, c'est un problème de conception. Le terme "couplage lâche" est intimement lié à l'état souhaitable de "forte cohésion", qui est opposé mais complémentaire.

Un couplage lâche signifie simplement que les éléments de conception individuels doivent être construits de manière à réduire la quantité d'informations inutiles dont ils ont besoin sur les autres éléments de conception.

Une cohésion élevée ressemble un peu à un "couplage étroit", mais une cohésion forte est un état dans lequel les éléments de conception qui doivent réellement se connaître sont conçus de manière à fonctionner ensemble de manière propre et élégante.

Le fait est que certains éléments de conception doivent connaître les détails d’autres éléments de conception, ils doivent donc être conçus de cette manière, et non accidentellement. Les autres éléments de conception ne doivent pas connaître les détails des autres éléments de conception. Ils doivent donc être conçus de cette manière, à dessein, plutôt que de manière aléatoire.

La mise en œuvre est laissée comme un exercice pour le lecteur :).

37
David M. Karr

Le code étroitement couplé repose sur une implémentation concrète. Si j'ai besoin d'une liste de chaînes dans mon code et que je la déclare comme ceci (en Java)

ArrayList<String> myList = new ArrayList<String>();

alors je suis dépendant de l'implémentation ArrayList.

Si je veux changer cela en code faiblement couplé, je fais de ma référence un type d'interface (ou autre abstrait).

List<String> myList = new ArrayList<String>();

Cela m'empêche d'appeler la méthode toute sur myList qui est spécifique à l'implémentation ArrayList. Je suis limité aux seules méthodes définies dans l'interface List. Si je décide par la suite que j'ai vraiment besoin d'une LinkedList, il me suffit de modifier mon code à un endroit où j'ai créé la nouvelle liste, et non à 100 endroits où j'ai passé des appels à des méthodes ArrayList.

Bien sûr, vous pouvez instancier une ArrayList en utilisant la première déclaration et vous empêcher de n’utiliser aucune méthode ne faisant pas partie de l’interface List, mais en utilisant la deuxième déclaration, le compilateur vous gardera honnête.

33
Bill the Lizard

Le degré de différence entre les réponses ici montre pourquoi ce serait un concept difficile à saisir mais à dire aussi simplement que je peux le décrire:

Pour que je sache que si je vous lance une balle, vous pouvez l'attraper, je n'ai vraiment pas besoin de savoir quel âge vous avez. Je n'ai pas besoin de savoir ce que vous avez mangé au petit-déjeuner et je me fiche de savoir qui était votre premier béguin. Tout ce que j'ai besoin de savoir, c'est que vous pouvez attraper. Si je le sais, je ne me soucie pas de savoir si c'est vous que je vous lance à vous ou à votre frère.

Avec des langages non dynamiques comme c # ou Java etc., nous le faisons via des interfaces. Disons donc que nous avons l'interface suivante:

public ICatcher
{
   public void Catch();
}

Et maintenant, disons que nous avons les classes suivantes:

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

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

}

Désormais, CatcherA et CatcherB implémentent tous deux la méthode Catch. Le service nécessitant un Catcher peut donc utiliser l’une ou l’autre de ces méthodes, sans se soucier de savoir lequel. Ainsi, un service étroitement couplé pourrait instancier directement une capture, c'est-à-dire.

public CatchService
{
   private CatcherA catcher = new CatcherA();

   public void CatchService()
   {
      catcher.Catch();
   }

}

Ainsi, CatchService peut faire exactement ce qu’il s’est fixé, mais il utilise CatcherA et utilisera toujours CatcherA. C'est dur à coder, donc c'est rester là jusqu'à ce que quelqu'un vienne le refacturer.

Prenons maintenant une autre option, appelée injection de dépendance:

public CatchService
{
   private ICatcher catcher;

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

Ainsi, les cals qui installent CatchService peuvent effectuer les opérations suivantes:

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

ou

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

Cela signifie que le service Catch n'est pas étroitement couplé à CatcherA ou CatcherB.

Il existe plusieurs autres stratégies pour coupler vaguement des services tels que l'utilisation d'un cadre IoC, etc.

25
Owen

Vous pouvez penser que le couplage (serré ou lâche) représente littéralement la quantité d'effort qu'il faudrait pour séparer une classe particulière de sa dépendance à une autre classe. Par exemple, si toutes les méthodes de votre classe avaient un petit bloc final où vous avez appelé Log4Net pour enregistrer quelque chose, vous diriez que votre classe était étroitement liée à Log4Net. Si votre classe contenait plutôt une méthode privée appelée LogSomething qui était le seul endroit à appeler le composant Log4Net (et les autres méthodes toutes appelées LogSomething à la place), vous diriez alors que votre classe était couplée de manière lâche à Log4Net (car cela ne prendrait pas beaucoup effort pour extraire Log4Net et le remplacer par quelque chose d’autre).

23
MusiGenesis

Définition

Essentiellement, le couplage est la mesure dans laquelle un objet ou un ensemble d'objets donné s'appuie sur un autre objet ou un autre ensemble d'objets pour accomplir sa tâche.

Haut couplage

Pensez à une voiture. Pour que le moteur démarre, une clé doit être insérée dans le contacteur, tourner, de l'essence doit être présente, un spark doit se produire, les pistons doivent se déclencher et le moteur doit s'animer. Vous On pourrait dire qu’un moteur de voiture est fortement couplé à plusieurs autres objets: un couplage élevé, mais ce n’est pas vraiment une mauvaise chose.

Couplage lâche

Pensez à un contrôle utilisateur pour une page Web chargée de permettre aux utilisateurs de publier, éditer et afficher un certain type d'informations. Le contrôle unique peut être utilisé pour permettre à un utilisateur de publier une nouvelle information ou d’éditer une nouvelle information. Le contrôle doit pouvoir être partagé entre deux chemins différents - new et edit. Si le contrôle est écrit de telle sorte qu'il ait besoin d'un certain type de données provenant des pages qui le contiendront, alors vous pourriez dire qu'il est trop fortement couplé. Le contrôle ne doit plus avoir besoin de sa page de conteneur.

12
Tom

C'est un concept assez général, donc les exemples de code ne donneront pas une image complète.

Un type ici au travail m'a dit: "Les motifs sont comme des fractales, vous pouvez les voir lorsque vous effectuez un zoom très rapproché et lorsque vous effectuez un zoom sur le niveau de l'architecture."

La lecture de la brève page wikipedia peut vous donner une idée de cette généralité:

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

En ce qui concerne un exemple de code spécifique ...

Voici un couplage lâche avec lequel j'ai travaillé récemment, dans les fichiers Microsoft.Practices.CompositeUI.

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

Ce code déclare que cette classe a une dépendance sur un CustomizableGridService. Au lieu de simplement référencer directement l'implémentation exacte du service, elle indique simplement que cela nécessite une mise en œuvre QUELQUEQUE de ce service. Ensuite, au moment de l'exécution, le système résout cette dépendance.

Si ce n'est pas clair, vous pouvez lire une explication plus détaillée ici:

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

Imaginez que ABCCustomizableGridService soit l’implémentation que j’ai l’intention de faire ici.

Si je le souhaite, je peux le retirer et le remplacer par XYZCustomizableGridService ou StubCustomizableGridService sans aucun changement de la classe avec cette dépendance.

Si j'avais directement référencé ABCCustomizableGridService, il me faudrait alors modifier cette/ces référence (s) afin de permuter une autre implémentation de service.

7
rice

Le couplage concerne les dépendances entre systèmes, qui peuvent être des modules de code (fonctions, fichiers ou classes), des outils dans un pipeline, des processus serveur-client, etc. Moins les dépendances sont générales, plus elles deviennent "étroitement couplées", car la modification d'un système nécessitait de modifier les autres systèmes qui en dépendent. La situation idéale est le "couplage lâche" dans lequel un système peut être changé et les systèmes qui en dépendent continueront à fonctionner sans modification.

La manière générale de réaliser un couplage lâche consiste à utiliser des interfaces bien définies. Si l'interaction entre deux systèmes est bien définie et respectée des deux côtés, il devient alors plus facile de modifier un système tout en veillant à ce que les conventions ne soient pas enfreintes. Dans la pratique, il est fréquent que l’on ne crée pas d’interface bien définie, ce qui se traduit par une conception peu soignée et un couplage étroit.

Quelques exemples:

  • L'application dépend d'une bibliothèque. Sous couplage étroit, l'application rompt avec les nouvelles versions de la lib. Google pour "DLL Hell".

  • L'application client lit les données d'un serveur. Sous couplage étroit, les modifications apportées au serveur nécessitent des corrections du côté client.

  • Deux classes interagissent dans une hiérarchie orientée objet. Sous couplage étroit, les modifications apportées à une classe nécessitent que l’autre classe soit mise à jour pour correspondre.

  • Plusieurs outils de ligne de commande communiquent dans un canal. S'ils sont étroitement liés, les modifications apportées à la version d'un outil de ligne de commande entraîneront des erreurs dans les outils qui lisent sa sortie.

6
Parappa

Le couplage fait référence à la manière dont différentes classes sont connectées les unes aux autres. Les classes étroitement couplées contiennent un grand nombre d'interactions et de dépendances.

Les classes faiblement couplées sont à l'opposé en ce que leurs dépendances entre elles sont réduites au minimum et s'appuient plutôt sur des interfaces publiques bien définies les unes des autres.

Legos, les jouets qui SNAP ensemble seraient considérés comme faiblement couplés car vous pouvez simplement assembler les pièces et construire le système de votre choix. Cependant, un puzzle Le puzzle a des pièces qui sont étroitement liées. Vous ne pouvez pas prendre une pièce d’un puzzle (système) et la casser dans un puzzle différent, car le système (puzzle) est très dépendant des pièces très spécifiques construites spécifiquement pour cela "design" particulier. Les legos sont construits de manière plus générique de manière à pouvoir être utilisés dans votre Lego House ou dans mon Lego Alien Man.

Référence: https://megocode3.wordpress.com/2008/02/14/coupling-and-cohesion/

5
user1263981

Deux composants sont fortement couplés lorsqu'ils dépendent d'une mise en œuvre concrète l'un de l'autre.

Supposons que j'ai ce code quelque part dans une méthode de ma classe:

this.some_object = new SomeObject();

Maintenant, ma classe dépend de SomeObject, et ils sont hautement couplés. D'autre part, supposons que j'ai une méthode InjectSomeObject:

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

Ensuite, le premier exemple peut simplement utiliser SomeObject injecté. Ceci est utile pendant les tests. En fonctionnement normal, vous pouvez utiliser des classes lourdes utilisant la base de données, le réseau, etc. tout en passant des tests légers et factices. la mise en oeuvre. Avec un code étroitement couplé, vous ne pouvez pas faire cela.

Vous pouvez faciliter certaines tâches en utilisant des conteneurs d’injection de dépendance. Vous pouvez en savoir plus sur DI sur Wikipedia: http://en.wikipedia.org/wiki/Dependency_injection .

Il est parfois facile d'aller trop loin. À un moment donné, vous devez rendre les choses concrètes, sinon votre programme sera moins lisible et moins compréhensible. Utilisez donc ces techniques principalement au niveau des composants et sachez ce que vous faites. Assurez-vous de tirer parti d'un couplage lâche. Sinon, vous n'en aurez probablement pas besoin à cet endroit. DI peut rendre votre programme plus complexe. Assurez-vous de faire un bon compromis. En d'autres termes, maintenez un bon équilibre. Comme toujours lors de la conception de systèmes. Bonne chance!

4
Paweł Hajdan

En informatique, il y a une autre signification pour "couplage lâche" que personne d'autre n'a posté ici, alors ... Ça y est - espérons que vous me donnerez des votes jusqu'à ce que cela ne soit pas perdu au fond du tas! Sûrement le sujet de ma réponse appartient à toute réponse complète à la question ... À savoir:

Le terme "couplage en vrac" a été introduit pour la première fois en informatique en tant que terme utilisé en tant qu’adjectif concernant l’architecture de la CPU dans une configuration à plusieurs CPU. Son équivalent est "couplage étroit". Le couplage lâche est le moment où les processeurs ne partagent pas beaucoup de ressources en commun et le couplage étroit, le cas échéant.

Le terme "système" peut être déroutant ici, alors analysez bien la situation.

Habituellement, mais pas toujours, plusieurs processeurs dans une configuration matérielle dans laquelle ils existent dans un système (comme dans les boîtiers "PC" individuels) seraient étroitement couplés. À l'exception de certains systèmes ultra-performants dotés de sous-systèmes partageant la mémoire principale d'un système à un autre, tous les systèmes divisibles sont couplés de manière lâche.

Les termes "Tightly Coupled" et "Loosely Coupled" ont été introduits avant les processeurs multi-threadés et multi-cœurs ont été inventés. Ces termes peuvent donc nécessiter des compagnons pour bien cerner la situation actuelle. Et, en effet, on peut très bien avoir aujourd'hui un système qui englobe les deux types dans un système global. En ce qui concerne les systèmes logiciels actuels, il existe deux architectures communes, une de chaque variété, qui sont assez communes, elles devraient être connues.

Premièrement, puisqu'il s'agissait de la question, quelques exemples de systèmes à couplage lâche:

  • VaxClusters
  • Clusters Linux

En revanche, certains exemples étroitement liés:

  • Systèmes d'exploitation semétriques à traitements multiples (SMP) - p. Ex. Fedora 9
  • CPU multi-threadés
  • CPU multi-coeurs

Dans l'informatique d'aujourd'hui, il n'est pas rare que les deux systèmes fonctionnent dans un même système global. Par exemple, prenons les processeurs Pentium modernes à deux ou quatre cœurs exécutant Fedora 9 - il s’agit de systèmes informatiques à couplage étroit. Ensuite, combinez plusieurs d'entre eux dans un cluster Linux à couplage lâche et vous disposez désormais d'une informatique à couplage lâche et étroit! Oh, le matériel moderne n'est-il pas merveilleux!

3
Richard T

En langage simple, couplé de manière lâche signifie que cela ne dépend pas d’un autre événement pour se produire. Il s'exécute indépendamment.

3
Ravindra Miyani

Considérons une application Windows avec FormA et FormB. FormA est le formulaire principal et affiche FormB. Imaginez FormB ayant besoin de transmettre des données à son parent.

Si vous avez fait ceci:

class FormA 
{
    FormB fb = new FormB( this );

    ...
    fb.Show();
}

class FormB 
{
    FormA parent;

    public FormB( FormA parent )
    {
        this.parent = parent;
    }     
}

FormB est étroitement associé à FormA. FormB ne peut avoir d'autre parent que celui de type FormA.

Si, par contre, vous avez demandé à FormB de publier un événement et que FormA soit abonné à cet événement, FormB pourrait alors renvoyer des données via cet événement à son abonné. Dans ce cas, FormB ne sait même pas qu’il répond à son parent; Grâce au couplage lâche, l'événement fournit simplement un dialogue avec les abonnés. N'importe quel type peut maintenant être un parent de FormA.

rp

3
rp.

Je propose un test très simple de couplage de code :

  1. La pièce A du code est étroitement liée à la pièce B du code s'il existe une modification possible de la pièce B qui imposerait des modifications dans la pièce A afin de conserver son exactitude.

  2. La pièce A du code n'est pas étroitement couplée à la pièce B du code s'il n'y a aucune modification possible de la pièce B qui rendrait nécessaire un changement à la pièce A.

Cela vous aidera à vérifier le couplage existant entre les éléments de votre code. pour le raisonnement, voir cet article de blog: http://marekdec.wordpress.com/2012/11/14/loose-coupling-tight-coupling-decoupling-what-is-that-all-about/

2
Marek Dec

Lorsque vous créez un objet d'une classe en utilisant le mot clé new dans une autre classe, vous effectuez un couplage étroit (mauvaise pratique) à la place, utilisez plutôt un couplage lâche, ce qui est une bonne pratique.

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

}
}

Explication:

Dans l'exemple ci-dessus, nous avons deux classes A et B

La classe B implémente l’interface i.e. InterfaceClass.

InterfaceClass définit un contrat pour la classe B car InterfaceClass dispose de méthodes abstraites de la classe B auxquelles toute autre classe, par exemple A., peut accéder.

Dans la classe A, nous avons la méthode display qui peut excepté l’objet de la classe qui implémente InterfaceClass (dans notre cas c’est la classe B). Et sur cet objet, la méthode de la classe A appelle display () et getVar () de la classe B

Dans MainClass, nous avons créé un objet de classe A et B. Nous avons également appelé la méthode d’affichage de A en transmettant un objet de classe B, c’est-à-dire objb. La méthode d'affichage de A sera appelée avec l'objet de la classe B.

Parlons maintenant du couplage lâche. Supposons qu'à l'avenir vous deviez changer le nom de la classe B en ABC, vous n'avez pas besoin de changer son nom dans la méthode d'affichage de la classe B, créez simplement l'objet de nouveau (classe ABC) et transmettez-le à la méthode d'affichage dans MailClass. Vous n'avez rien à changer en classe A

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

2
Abhishek Aggarwal

Quelques réponses longues ici. Le principe est cependant très simple. Je soumets la déclaration liminaire de wikipedia :

"Le couplage lâche décrit une relation résiliente entre deux ou plusieurs systèmes ou organisations avec une sorte de relation d'échange.

Chaque fin de la transaction rend ses exigences explicites et émet peu d'hypothèses sur l'autre extrémité. "

2
Ben

Vous pouvez en savoir plus sur le concept générique de "couplage lâche" .

En bref, il s'agit d'une description d'une relation entre deux classes, où chaque classe en sait le moins sur l'autre et chaque classe pourrait éventuellement continuer à fonctionner correctement, que l'autre soit présente ou non et sans dépendance vis-à-vis de l'implémentation particulière de l'autre. classe.

0
Franci Penov