Quelle est la signification de «haute cohésion»?

28

Je suis un étudiant qui a récemment rejoint une entreprise de développement de logiciels en tant que stagiaire. De retour à l'université, un de mes professeurs avait l'habitude de dire que nous devons nous efforcer de réaliser "un faible couplage et une forte cohésion".

Je comprends la signification du faible couplage. Cela signifie garder le code de composants séparés séparément, de sorte qu'un changement à un endroit ne casse pas le code à un autre.

Mais qu'entend-on par haute cohésion. Si cela signifie bien intégrer les différentes pièces d'un même composant, je ne comprends pas comment cela devient avantageux.

Qu'entend-on par cohésion élevée? Peut-on expliquer un exemple pour comprendre ses avantages?

Max
la source
doublon possible de Existe
moucher
1
L'article de Wikipédia ne répond-il pas suffisamment à votre question? en.wikipedia.org/wiki/Cohesion_(computer_science)
Eoin Carroll
Il y a un bon article à ce sujet sur: msdn.microsoft.com/en-us/magazine/cc947917.aspx
NoChance
1
@EoinCarroll: Malheureusement, l'article de wikipedia pour le moment ne donne pas de bons exemples concrets avec lesquels les nouveaux programmeurs peuvent travailler. La théorie est bonne et tout, mais ne colle pas vraiment jusqu'à ce que vous ayez fait les erreurs entourant la faible cohésion. La haute cohésion est l'un de ces sujets qui m'a pris plusieurs années de programmation pour bien comprendre pourquoi c'est important et comment y parvenir.
Spoike
Je n'ai jamais vraiment compris la cohésion avant d'avoir lu Clean Code. Tu devrais aussi.
Sebastian Redl

Réponses:

25

Une façon de considérer la cohésion en termes d'OO est de savoir si les méthodes de la classe utilisent l'un des attributs privés. En utilisant des métriques telles que LCOM4 (manque de méthodes cohésives), comme souligné par gnat dans cette réponse ici , vous pouvez identifier les classes qui pourraient être refactorisées. La raison pour laquelle vous souhaitez refactoriser des méthodes ou des classes pour qu'elles soient plus cohérentes est que cela rend la conception du code plus simple pour les autres utilisateurs . Croyez-moi; la plupart des responsables techniques et des programmeurs de maintenance vous adoreront lorsque vous corrigerez ces problèmes.

Vous pouvez utiliser des outils dans votre processus de génération tels que Sonar pour identifier une faible cohésion dans la base de code. Il y a quelques cas très courants auxquels je peux penser où les méthodes ont une faible "cohésion" :

Cas 1: la méthode n'est pas du tout liée à la classe

Prenons l'exemple suivant:

public class Food {
   private int _foodValue = 10;

   public void Eat() {
     _foodValue -= 1;
   }

   public void Replenish() {
     _foodValue += 1;
   }

   public void Discharge() {
     Console.WriteLine("Nnngghhh!");
   }
}

L'une des méthodes Discharge(),, manque de cohésion car elle ne touche aucun membre privé de la classe. Dans ce cas , il n'y a qu'un seul membre privé: _foodValue. S'il ne fait rien avec les internes de la classe, est-ce qu'il y appartient vraiment? La méthode pourrait être déplacée vers une autre classe qui pourrait être nommée par exemple FoodDischarger.

// Non-cohesive function extracted to another class, which can
// be potentially reused in other contexts
public FoodDischarger {
  public void Discharge() {
    Console.WriteLine("Nnngghhh!");
  }
}

Lorsque vous le faites en Javascript, puisque les fonctions sont des objets de première classe, la décharge peut être une fonction libre:

function Food() {
    this._foodValue = 10;
}
Food.prototype.eat = function() {
    this._foodValue -= 1;
};
Food.prototype.replenish = function() {
    this._foodValue += 1;
};

// This
Food.prototype.discharge = function() {
    console.log('Nnngghhh!');
};
// can easily be refactored to:
var discharge = function() {
    console.log('Nnngghhh!');
};
// making it easily reusable without creating a class

Cas 2: Classe utilitaire

Il s'agit en fait d'un cas courant qui rompt la cohésion. Tout le monde aime les classes utilitaires, mais celles-ci indiquent généralement des défauts de conception et rendent la base de code plus difficile à maintenir la plupart du temps (en raison de la forte dépendance associée aux classes utilitaires). Considérez les classes suivantes:

public class Food {
    public int FoodValue { get; set; }
}

public static class FoodHelper {

    public static void EatFood(Food food) {
        food.FoodValue -= 1;
    }

    public static void ReplenishFood(Food food) {
        food.FoodValue += 1;
    }

}

Ici, nous pouvons voir que la classe d'utilité doit accéder à une propriété de la classe Food. Les méthodes de la classe d'utilité n'ont aucune cohésion dans ce cas car elles ont besoin de ressources extérieures pour faire leur travail. Dans ce cas, ne serait-il pas préférable d'avoir les méthodes dans la classe avec laquelle elles travaillent (un peu comme dans le premier cas)?

Cas 2b: objets cachés dans les classes utilitaires

Il existe un autre cas de classes d'utilitaires où il existe des objets de domaine non réalisés. La première réaction instinctive d'un programmeur lors de la programmation de la manipulation de chaînes consiste à lui écrire une classe utilitaire. Comme celle qui valide ici quelques représentations de chaîne courantes:

public static class StringUtils {

  public static bool ValidateZipCode(string zipcode) {
    // validation logic
  }

  public static bool ValidatePhoneNumber(string phoneNumber) {
    // validation logic
  }

}

Ce que la plupart ne réalisent pas ici, c'est qu'un code postal, un numéro de téléphone ou toute autre représentation de chaîne peut être un objet lui-même:

public class ZipCode {
    private string _zipCode;
    public bool Validates() {
      // validation logic for _zipCode
    }
}

public class PhoneNumber {
    private string _phoneNumber;
    public bool Validates() {
      // validation logic for _phoneNumber
    }
}

La notion selon laquelle vous ne devez pas "gérer les chaînes" directement est détaillée dans cet article de blog par @codemonkeyism , mais est étroitement liée à la cohésion, car la façon dont les programmeurs utilisent les chaînes en mettant la logique dans les classes utilitaires.

Spoike
la source
Maintenant, si seulement nous pouvions obtenir nos ORM pour gérer correctement nos classes ZipCode et PhoneNumber personnalisées: |
Pete
+1 de bons exemples. Pour le dernier point, voir aussi sourcemaking.com/refactoring/primitive-obsession
AlexFoxGill
@Pete Votre ORM le gérera si vous fournissez des opérateurs de conversion de la chaîne au type défini (ZipCode, PhoneNumber): msdn.microsoft.com/en-us/library/85w54y0a.aspx
Marcus
9

Une cohésion élevée signifie garder des choses similaires et connexes ensemble, pour coupler ou fusionner des pièces qui partagent le contenu, la fonctionnalité, la raison ou l'objectif . En d'autres termes, une faible cohésion pourrait par exemple signifier une fonction / classe / entité de code qui sert des objectifs multiples plutôt que d'être " au point ". L'une des idées porteuses est de faire une chose et de bien le faire . D'autres pourraient inclure le fait évident que vous ne reproduisez pas des fonctionnalités similaires dans de nombreux endroits. Cela améliore également la localisation de la base de code, certains types de choses se trouvent à un certain endroit (fichier, classe, ensemble de fonctions, ...) plutôt que d'être dispersés.

Par exemple, considérons une classe qui sert deux ou trois objectifs: Charge / stocke des ressources (par exemple un fichier), puis analyse et affiche le contenu. Une telle classe a une faible cohésion car elle gère au moins deux tâches distinctes qui ne sont pas du tout liées (E / S de fichiers, analyse et affichage). Une conception à haute cohésion pourrait utiliser des classes distinctes pour charger et stocker la ressource, l'analyser puis l'afficher.

D'un autre côté, le faible couplage vise à séparer les éléments distincts - afin qu'ils interagissent le moins possible, ce qui réduit la complexité et simplifie la conception.

zxcdw
la source
7

Cela signifie que les parties d'un objet donné sont étroitement liées à la fonction de l'objet. Cela signifie qu'il y a très peu ou pas de déchets dans l'objet en termes de fonction ou de responsabilité. Cela peut à son tour améliorer la compréhension de l'utilisation de l'objet en question.

Ingénieur du monde
la source
cela ne s'applique-t-il pas également à un niveau supérieur à celui des objets? Par exemple, regrouper des objets / fonctions liés à une tâche dans des espaces de noms?
stijn