Un modo per osservare 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 , puoi identificare le classi che potrebbero essere refactored. Il motivo per cui si desidera ridefinire metodi o classi per essere più coerenti è che rende più semplice la progettazione del codice perché altri lo utilizzino . Fidati di me; la maggior parte dei lead tecnologici e dei programmatori di manutenzione ti adoreranno quando risolvi questi problemi.
Puoi utilizzare gli strumenti nel processo di compilazione come Sonar per identificare una bassa coesione nella base di codice. Ci sono un paio di casi molto comuni a cui posso pensare dove i metodi sono bassi in "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 c'è un solo membro privato: _foodValue
. Se non fa nulla con gli interni della classe, allora ci appartiene davvero? Il metodo potrebbe essere spostato in un'altra classe che potrebbe essere denominata ad esempio FoodDischarger
.
// Non-cohesive function extracted to another class, which can
// be potentially reused in other contexts
public FoodDischarger {
public void Discharge() {
Console.WriteLine("Nnngghhh!");
}
}
In te lo stai facendo in Javascript, dal momento che 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. Ognuno di noi ama classi di utilità, ma di solito indicano difetti di progettazione e la maggior parte delle volte rende il codice più complesso 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à ha bisogno di accedere a una proprietà nella classe Food
. I metodi nella classe di utilità non hanno affatto coesione in questo caso perché ha bisogno di risorse esterne per fare il suo lavoro. In questo caso, non sarebbe meglio avere i metodi nella classe con cui stanno lavorando (molto come nel primo caso)?
Caso 2b: oggetti nascosti nelle classi di utilità
C'è un altro caso di classi di utilità in cui sono presenti oggetti di dominio non realizzati. La prima reazione istintiva che un programmatore ha quando programmando la manipolazione delle stringhe è di scrivere una classe di utilità per questo. 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 si rende conto 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 dovrebbe "gestire le stringhe" direttamente è descritta in questo blogpost di @codemonkeyism , ma è strettamente correlato alla coesione perché il modo in cui i programmatori usano le stringhe inserendo la logica nelle classi di utilità.