Що означає "висока згуртованість"?


28

Я студент, який нещодавно приєднався до компанії з розробки програмного забезпечення як стажист. Ще в університеті один з моїх професорів казав, що ми повинні прагнути досягти "низької зв'язку та високої згуртованості".

Я розумію значення низької зв'язку. Це означає тримати код окремих компонентів окремо, щоб зміна одного місця не зламала код в іншому.

Але що означає висока згуртованість. Якщо це означає добре інтегрувати різні частини одного компонента між собою, я не розумію, як це стає вигідним.

Що означає висока згуртованість? Чи можна пояснити приклад, щоб зрозуміти його переваги?



1
Чи не відповідає стаття вікіпедії вашим питанням достатньо? en.wikipedia.org/wiki/Cousion_(computer_science)
Eoin Carroll

Про це є хороша стаття за адресою: msdn.microsoft.com/en-us/magazine/cc947917.aspx
NoChance

1
@EoinCarroll: На жаль, стаття Вікіпедії на даний момент не дає жодних хороших конкретних прикладів, з якими можуть працювати нові програмісти. Теорія хороша і все, але насправді не дотримується, поки ви не зробите помилок, пов’язаних із низькою згуртованістю. Висока згуртованість - одна з тих тем, яка потребувала декількох років програмування, щоб повністю зрозуміти, чому це важливо і як цього досягти.
Спойк

Я ніколи не розумів згуртованості, поки не прочитав «Чистий кодекс». Ви також повинні.
Себастьян Редл

Відповіді:


25

Один із способів розгляду згуртованості з точки зору ОО - це, якщо методи в класі використовують будь-який із приватних атрибутів. Використовуючи такі показники, як 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 , але тісно пов'язане з згуртованістю, оскільки спосіб програмістів використовує рядки, вводячи логіку в класах утиліти.


Тепер якби ми могли отримати наші ORM, щоб правильно обробляти наші спеціальні класи ZipCode та PhoneNumber: |
Піт

+1 хороших прикладів. Для останньої точки, дивись також sourcemaking.com/refactoring/primitive-obsession
AlexFoxGill

@Pete Ваш ORM буде обробляти його , якщо ви надаєте оператори перетворення з рядка в ваш певного типу (ZipCode, PhoneNumber): msdn.microsoft.com/en-us/library/85w54y0a.aspx
Маркуса

9

Висока згуртованість означає зберігати подібні та споріднені речі разом, з'єднувати чи сплавляти частини, які діляться вмістом, функціональністю, причиною чи метою . Іншими словами, низька згуртованість може, наприклад, означати функцію / клас / сутність коду, яка служить численним цілям, а не " до точки ". Однією з ідей ідеї є зробити одне і зробити це добре . Інші можуть включати очевидний факт, що ви не повторюєте подібну функціональність у багатьох місцях. Це також покращує локальність бази коду, певні речі знаходяться в певному місці (файл, клас, набір функцій, ...), а не розкидаються по всьому.

Як приклад, розглянемо клас, який виконує дві або три цілі: завантажує / зберігає ресурси (наприклад, файл), а потім аналізує та показує вміст. Такий клас має низьку згуртованість, оскільки він управляє щонайменше двома окремими завданнями, які взагалі не пов'язані (введення / виведення файлів, аналіз та показ). Висока конструкція згуртованості може використовувати різні класи для завантаження та зберігання ресурсу, їх аналізу та подальшого відображення.

З іншого боку, низька муфта спрямована на те, щоб відрізняти різні речі - так, щоб вони взаємодіяли між собою якомога менше, що потім зменшує складність та спрощує конструкцію.


7

Це означає, що частини даного об’єкта тісно пов'язані з функцією об'єкта. Це означає, що в об’єкті дуже мало або взагалі немає відходів з точки зору функціонування або відповідальності. Це, в свою чергу, може покращити розуміння того, для чого слід використовувати відповідний об'єкт.


чи це також не стосується більш високого рівня, ніж просто об'єкти? наприклад, групування об'єктів / функцій, пов'язаних із завданням, в просторах імен?
стихій
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.