Один із способів розгляду згуртованості з точки зору ОО - це, якщо методи в класі використовують будь-який із приватних атрибутів. Використовуючи такі показники, як LCOM4 (Відсутність згуртованих методів), на що вказує gnat у цій відповіді тут , ви можете визначити класи, які можуть бути відновлені. Причина, за якою ви хочете, щоб методи рефакторингу чи класи були більш згуртованими, це те, що це спрощує дизайн коду для інших, щоб використовувати його . Довірся мені; Більшість технічних провідників та програмістів з технічного обслуговування полюблять вас, коли виправите ці проблеми.
Ви можете використовувати інструменти у процесі збирання, такі як Sonar, для виявлення низької згуртованості в кодовій базі. Є кілька дуже поширених випадків, які я можу придумати, де методи мають низьку «згуртованість» :
Випадок 1: Метод взагалі не пов'язаний з класом
Розглянемо наступний приклад:
public class Food {
private int _foodValue = 10;
public void Eat() {
_foodValue -= 1;
}
public void Replenish() {
_foodValue += 1;
}
public void Discharge() {
Console.WriteLine("Nnngghhh!");
}
}
Одним із методів Discharge()
не вистачає згуртованості, оскільки він не торкається жодного з приватних членів класу. В цьому випадку є тільки один приватний член: _foodValue
. Якщо він нічого не робить із внутрішніми класами, то чи справді він там належить? Метод може бути переміщений в інший клас , який можна було б назвати , наприклад FoodDischarger
.
// Non-cohesive function extracted to another class, which can
// be potentially reused in other contexts
public FoodDischarger {
public void Discharge() {
Console.WriteLine("Nnngghhh!");
}
}
Ви робите це в Javascript, оскільки функції є першокласними об'єктами, розряд може бути вільною функцією:
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
Випадок 2: Клас корисності
Це насправді поширений випадок, який порушує згуртованість. Всі люблять утилітні класи, але вони зазвичай вказують на недоліки дизайну, і більшість часу робить базу коду складнішою для підтримки (через високу залежність, пов’язану з класами утиліти). Розглянемо наступні класи:
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;
}
}
Тут ми можемо побачити, що клас утиліти потребує доступу до властивості класу Food
. У цьому випадку методи класу корисності взагалі не мають згуртованості, оскільки для його роботи потрібні зовнішні ресурси. У цьому випадку, чи не було б краще мати методи в класі, з яким вони працюють самі (як у першому випадку)?
Випадок 2b: Приховані об'єкти в утилітах
Є ще один випадок класів корисних програм, де є нереалізовані об’єкти домену. Першою реакцією програміста при програмуванні рядкових маніпуляцій є написання для нього класу корисності. Як і те, що тут підтверджує пару загальних рядкових представлень:
public static class StringUtils {
public static bool ValidateZipCode(string zipcode) {
// validation logic
}
public static bool ValidatePhoneNumber(string phoneNumber) {
// validation logic
}
}
Що більшість не розуміє тут, це те, що поштовий індекс, номер телефону або будь-яке інше відображення рядків може бути самим об'єктом:
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
}
}
Поняття, що ви не повинні "обробляти рядки" безпосередньо, у цьому блозі докладно описано @codemonkeyism , але тісно пов'язане з згуртованістю, оскільки спосіб програмістів використовує рядки, вводячи логіку в класах утиліти.