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.