Un modo di considerare la coesione in termini di OO è se i metodi nella classe utilizzano uno degli attributi privati. Utilizzando metriche come LCOM4 (Mancanza di metodi coesivi), come sottolineato da Gnat in questa risposta qui , è possibile identificare le classi che potrebbero essere refactored. Il motivo per cui si desidera riformattare i metodi o le classi in modo che siano più coerenti è che semplifica la progettazione del codice per gli altri . Fidati di me; la maggior parte dei lead tecnologici e dei programmatori di manutenzione ti adoreranno quando risolvi questi problemi.
È possibile utilizzare strumenti nel processo di generazione come Sonar per identificare una bassa coesione nella base di codice. Ci sono un paio di casi molto comuni a cui riesco a pensare dove i metodi hanno poca "coesione" :
Caso 1: il metodo non è affatto correlato alla classe
Considera il seguente esempio:
public class Food {
private int _foodValue = 10;
public void Eat() {
_foodValue -= 1;
}
public void Replenish() {
_foodValue += 1;
}
public void Discharge() {
Console.WriteLine("Nnngghhh!");
}
}
Uno dei metodi Discharge()manca di coesione perché non tocca nessuno dei membri privati della classe. In questo caso v'è un solo membro privato: _foodValue. Se non fa nulla con gli interni della classe, allora appartiene davvero lì? Il metodo potrebbe essere spostato in un'altra classe che potrebbe essere denominata ad es FoodDischarger.
// Non-cohesive function extracted to another class, which can
// be potentially reused in other contexts
public FoodDischarger {
public void Discharge() {
Console.WriteLine("Nnngghhh!");
}
}
Nel farlo in Javascript, poiché le funzioni sono oggetti di prima classe, lo scarico può essere una funzione gratuita:
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
Caso 2: classe di utilità
Questo è in realtà un caso comune che rompe la coesione. Tutti amano le classi di utilità, ma di solito indicano difetti di progettazione e la maggior parte delle volte rende la base di codice più difficile da mantenere (a causa dell'alta dipendenza associata alle classi di utilità). Considera le seguenti classi:
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;
}
}
Qui possiamo vedere che la classe di utilità deve accedere a una proprietà nella classe Food. I metodi nella classe di utilità non hanno affatto coesione in questo caso perché hanno bisogno di risorse esterne per funzionare. In questo caso, non sarebbe meglio avere i metodi nella classe che stanno lavorando con se stesso (proprio come nel primo caso)?
Caso 2b: oggetti nascosti nelle classi di utilità
Esiste un altro caso di classi di utilità in cui sono presenti oggetti di dominio non realizzati. La prima reazione istintiva che un programmatore ha durante la programmazione della manipolazione di stringhe è quella di scrivere una classe di utilità per essa. Come quello qui che convalida un paio di rappresentazioni di stringhe comuni:
public static class StringUtils {
public static bool ValidateZipCode(string zipcode) {
// validation logic
}
public static bool ValidatePhoneNumber(string phoneNumber) {
// validation logic
}
}
Ciò che la maggior parte non capisce qui è che un codice postale, un numero di telefono o qualsiasi altra rappresentazione di stringa può essere un oggetto stesso:
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
}
}
L'idea che non si debba "gestire direttamente le stringhe" è dettagliata in questo post di @codemonkeyism , ma è strettamente correlata alla coesione perché il modo in cui i programmatori usano le stringhe inserendo la logica nelle classi di utilità.