Jednym ze sposobów spojrzenia na spójność w kategoriach OO jest to, czy metody w klasie używają któregokolwiek z prywatnych atrybutów. Korzystając z takich wskaźników, jak LCOM4 (brak spójnych metod), jak zauważył gnat w tej odpowiedzi tutaj , możesz zidentyfikować klasy, które można by refaktoryzować. Powodem, dla którego chcesz zmienić metody lub klasy, aby były bardziej spójne, jest to, że upraszcza to projektowanie kodu innym użytkownikom . Zaufaj mi; większość specjalistów technologicznych i programistów zajmujących się konserwacją pokocha Cię, gdy naprawisz te problemy.
Możesz użyć narzędzi w procesie kompilacji, takich jak Sonar, aby zidentyfikować niską spójność w bazie kodu. Istnieje kilka bardzo częstych przypadków, które mogę wymyślić, gdzie metody mają niską „spójność” :
Przypadek 1: Metoda w ogóle nie jest powiązana z klasą
Rozważ następujący przykład:
public class Food {
private int _foodValue = 10;
public void Eat() {
_foodValue -= 1;
}
public void Replenish() {
_foodValue += 1;
}
public void Discharge() {
Console.WriteLine("Nnngghhh!");
}
}
Jednej z metod Discharge()
brakuje spójności, ponieważ nie dotyka ona żadnego z prywatnych członków klasy. W tym przypadku istnieje tylko jeden prywatny użytkownik: _foodValue
. Jeśli nie ma to nic wspólnego z wewnętrznymi elementami klasy, to czy naprawdę tam jest? Metodę można przenieść do innej klasy, którą można nazwać np FoodDischarger
.
// Non-cohesive function extracted to another class, which can
// be potentially reused in other contexts
public FoodDischarger {
public void Discharge() {
Console.WriteLine("Nnngghhh!");
}
}
Robiąc to w JavaScript, ponieważ funkcje są obiektami najwyższej klasy, zrzut może być funkcją bezpłatną:
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
Przypadek 2: Klasa użyteczności
Jest to w rzeczywistości częsty przypadek, który niszczy spójność. Wszyscy uwielbiają klasy użytkowe, ale zwykle wskazują one na wady projektowe i przez większość czasu baza kodu jest trudniejsza do utrzymania (ze względu na wysoką zależność związaną z klasami użyteczności). Rozważ następujące klasy:
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;
}
}
Tutaj widzimy, że klasa narzędziowa musi mieć dostęp do właściwości w klasie Food
. Metody w klasie użyteczności nie mają w tym przypadku żadnej spójności, ponieważ do wykonania swojej pracy potrzebne są zasoby zewnętrzne. Czy w takim przypadku nie byłoby lepiej mieć metody w klasie, w której pracują same (podobnie jak w pierwszym przypadku)?
Przypadek 2b: Ukryte obiekty w klasach użytkowych
Istnieje inny przypadek klas narzędzi, w których znajdują się niezrealizowane obiekty domeny. Pierwszą reakcją szarpiącego kolana, jaką programista ma podczas programowania manipulacji łańcuchem, jest napisanie dla niego klasy użyteczności. Podobnie jak tutaj, który potwierdza kilka typowych reprezentacji ciągów:
public static class StringUtils {
public static bool ValidateZipCode(string zipcode) {
// validation logic
}
public static bool ValidatePhoneNumber(string phoneNumber) {
// validation logic
}
}
To, czego większość nie zdaje sobie sprawy, to to, że kod pocztowy, numer telefonu lub jakakolwiek inna reprezentacja ciągu może być samym obiektem:
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
}
}
Pojęcie, że nie powinieneś „bezpośrednio obsługiwać ciągów” jest szczegółowo opisane w tym blogu przez @codemonkeyism , ale jest ściśle związane ze spójnością, ponieważ sposób, w jaki programiści używają ciągów, umieszczając logikę w klasach narzędzi.