Una forma de ver la cohesión en términos de OO es si los métodos de la clase están utilizando alguno de los atributos privados. Usando métricas como LCOM4 (Falta de métodos cohesivos), como lo señala el mosquito en esta respuesta aquí , puede identificar clases que podrían refactorizarse. La razón por la que desea refactorizar métodos o clases para que sean más coherentes es que hace que el diseño del código sea más simple para que otros lo usen . Créeme; la mayoría de los líderes tecnológicos y programadores de mantenimiento lo amarán cuando solucione estos problemas.
Puede usar herramientas en su proceso de compilación como Sonar para identificar baja cohesión en la base del código. Hay un par de casos muy comunes en los que puedo pensar que los métodos tienen poca "cohesión" :
Caso 1: El método no está relacionado con la clase en absoluto.
Considere el siguiente ejemplo:
public class Food {
private int _foodValue = 10;
public void Eat() {
_foodValue -= 1;
}
public void Replenish() {
_foodValue += 1;
}
public void Discharge() {
Console.WriteLine("Nnngghhh!");
}
}
Uno de los métodos, Discharge()
carece de cohesión porque no toca a ninguno de los miembros privados de la clase. En este caso sólo hay un miembro privado: _foodValue
. Si no hace nada con los elementos internos de la clase, ¿realmente pertenece allí? El método podría moverse a otra clase que podría llamarse, por ejemplo FoodDischarger
.
// Non-cohesive function extracted to another class, which can
// be potentially reused in other contexts
public FoodDischarger {
public void Discharge() {
Console.WriteLine("Nnngghhh!");
}
}
Lo estás haciendo en Javascript, ya que las funciones son objetos de primera clase, la descarga puede ser una función 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
Caso 2: Clase de utilidad
Este es realmente un caso común que rompe la cohesión. Todo el mundo ama las clases de utilidad, pero esto generalmente indica fallas de diseño y la mayoría de las veces hace que la base de código sea más difícil de mantener (debido a la alta dependencia asociada con las clases de utilidad). Considere las siguientes clases:
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;
}
}
Aquí podemos ver que la clase de utilidad necesita acceder a una propiedad en la clase Food
. Los métodos en la clase de utilidad no tienen cohesión en absoluto en este caso porque necesita recursos externos para hacer su trabajo. En este caso, ¿no sería mejor tener los métodos en la clase con la que están trabajando (como en el primer caso)?
Caso 2b: objetos ocultos en clases de utilidad
Hay otro caso de clases de utilidad donde hay objetos de dominio no realizados. La primera reacción instintiva que tiene un programador al programar la manipulación de cadenas es escribir una clase de utilidad para ella. Como el que aquí valida un par de representaciones de cadenas comunes:
public static class StringUtils {
public static bool ValidateZipCode(string zipcode) {
// validation logic
}
public static bool ValidatePhoneNumber(string phoneNumber) {
// validation logic
}
}
Lo que la mayoría no se da cuenta aquí es que un código postal, un número de teléfono o cualquier otra repetición de cadena puede ser un objeto en sí mismo:
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 noción de que no debe "manejar cadenas" directamente se detalla en esta publicación de blog de @codemonkeyism , pero está estrechamente relacionada con la cohesión porque la forma en que los programadores usan cadenas al poner la lógica en las clases de utilidad.