JavaRush /Курси /C# SELF /Поради щодо стилю та читабельності ООП-коду

Поради щодо стилю та читабельності ООП-коду

C# SELF
Рівень 25 , Лекція 4
Відкрита

1. Вступ

«Чистий код» — це не якась священна корова, а реальний інструмент виживання програміста. У будь-якому, навіть найкрутішому, ООП-проєкті дуже швидко назбирується чимало класів, полів, методів, хитрих зв’язків… Якщо порушити естетику та структуру, за тиждень ваш же код стане нерозв’язним квестом. Детальніше про «боротьбу за виживання» можна прочитати у Роберта Мартіна, «Чистий код» — якраз про такі баталії.

Стиль у коді — це не про «кому як подобається», а про те, щоб усім було легше працювати:

  • Колеги (або ви самі) швидко зрозуміють, що відбувається.
  • Помилки (особливо архітектурні) видно одразу.
  • Код простіше допрацьовувати, припускаючись менше помилок.

Розгляньмо, як зробити ООП-код таким, щоб його любили рецензенти коду, колеги й навіть лінтери.

2. Імена: ваша перша лінія оборони

Іменування класів

Класи у C# прийнято називати в PascalCase (кожна нова частина слова — з великої літери, наприклад: MyNewClass) і так, щоб імʼя чітко відповідало на запитання «Що це?». Назви мають бути іменниками!

public class StudentAccount { /* ... */ }
public class InvoiceGenerator { /* ... */ }

Погано:

class doMagic { ... } // Погано: що за «магія»? Порушено PascalCase.

Іменування методів

Методи — теж у PascalCase, але тут краще використовувати дієслова + об’єкт дії:

public void PrintReport() { ... }
public string GetFormattedName() { ... }

Методи мають відображати дію (Print, Get, Save, Calculate тощо), щоб під час читання було зрозуміло, що відбудеться.

Іменування полів і властивостей

Поля зазвичай private, їх називають з маленької літери в camelCase, часто з підкресленням:

private int _count;
private Student _owner;

Властивості — у PascalCase, адже це частина зовнішнього інтерфейсу класу:

public int Balance { get; set; }

Змінні

Локальні змінні — у camelCase, настільки коротко, наскільки це доречно, і зрозуміло в контексті:

string inputName;
int studentCount;

Будь ласка, уникайте змінних на кшталт a1, result2, something — інакше ризикуєте влаштувати собі квест на наступний місяць.

3. Організація структури класу

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

Зазвичай розміщують так:

  1. Конструктори
  2. Властивості
  3. Методи
  4. Вкладені типи (enum, class тощо)

Приклад:


public class Student
{
    // --- Поля ---
    private string _name;

    // --- Конструктор ---
    public Student(string name)
    {
        _name = name;
    }

    // --- Властивості ---
    public string Name
    {
        get => _name;
        set => _name = value;
    }

    // --- Методи ---
    public void PrintInfo()
    {
        Console.WriteLine($"Імʼя: {_name}");
    }
}

Ці «блоки» зручно розділяти коментарями (// --- Методи ---), особливо у великих класах. JetBrains Rider, Visual Studio та інші IDE дають змогу швидко згортати/розгортати такі розділи.

4. Коментарі та документація

Коментарі — це добре. Але погано, якщо ними зловживати або писати «пояснення до незрозумілого коду», коли можна просто змінити сам код!

Гарний коментар — той, що пояснює «чому», а не «що».

// Використовуємо Guid як унікальний ідентифікатор, оскільки система розподілена
public Guid Id { get; set; }

Документація методів, класів і властивостей

Використовуйте XML-документацію для класів і публічних методів. IDE показуватимуть ці описи під час наведення курсора.


/// <summary>
/// Представляє студента університету.
/// </summary>
public class Student
{
    /// <summary>
    /// Імʼя студента.
    /// </summary>
    public string Name { get; set; }
}

Що коментувати не потрібно

  • Прості речі (i++ // збільшуємо i на 1).
  • Погано названі змінні («// тут щось відбувається» — але що саме?).

5. Розділяй і володарюй

Маленькі класи та методи

Золоте правило: один клас — одна відповідальність (див. Single Responsibility Principle). Якщо клас Student і облік оцінок веде, і електронну пошту обробляє, і розкладом керує — щось тут не так.

  • Класи до 300–400 рядків — це нормально. Більше — привід замислитися.
  • Методи до 15–20 рядків — читабельно. Винятки бувають, якщо це метод-обробник великого кейсу.

Приклад «роздутого» методу:


public void Process()
{
    // Сповіщаємо клієнта
    // Зберігаємо зміни
    // Надсилаємо електронний лист
    // Записуємо журнали
    // ... (15 кроків)
}

Краще:


public void Process()
{
    NotifyClient();
    SaveChanges();
    SendEmail();
    LogActivity();
}

Кожен із кроків винесено в окремий приватний метод — код стає компактнішим і легшим для тестування.

6. Корисні поради

Візуальна структура: форматування, відступи, порожні рядки

IDE можуть акуратно форматувати код автоматично (Ctrl+K, D у Visual Studio, Ctrl+Alt+L у Rider), але принципи все одно треба знати.

  • Відступи — 4 пробіли. Не табуляції, не два пробіли.
  • Порожні рядки — відділяйте методи один від одного, поля від властивостей, властивості від методів.
  • Дужки — завжди на новому рядку для класів і методів (стиль Allman):

public class Test
{
    public void Print()
    {
        Console.WriteLine("Привіт");
    }
}

«Сильні» і «слабкі» члени класу: модифікатори доступу

Завжди намагайтеся робити все максимально закритим: відкривайте лише те, що справді має бути доступне ззовні. Якщо поле чи метод потрібен лише всередині класу — робіть його private. Тільки якщо мають працювати нащадки — protected. public — лише для контрактів.

Погано:

public string ConnectionString; // Хто завгодно може змінити!

Краще:


private string _connectionString;
public string ConnectionString
{
    get => _connectionString;
    private set => _connectionString = value;
}

Використання автоматичних властивостей

З появою автоматичних властивостей і сетерів типу init-only писати «ручні» властивості стало недоречно.

Приклад:


public string Name { get; set; } // Чудово!
public int Age { get; init; }    // Тільки для ініціалізації, безпечніше.

А якщо потрібні обчислювані властивості:


public string FullName => $"{FirstName} {LastName}";

Інкапсуляція та гетери/сетери

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


private int _grade;
public int Grade
{
    get => _grade;
    set
    {
        if (value < 0) _grade = 0;
        else if (value > 100) _grade = 100;
        else _grade = value;
    }
}

Так ви не дасте собі чи іншим випадково «зламати» об’єкт.

7. Ще корисні поради

Не бійтеся інтерфейсів і абстракцій

Інтерфейси потрібні для зручних контрактів, тестування і розширення застосунку.

Погано:

  • інтерфейс з одним методом, що ніде не потрібен;
  • інтерфейс, який реалізує лише один клас.
Добре:
  • інтерфейс, який використовують два й більше класи;
  • інтерфейс для абстракції зовнішніх систем (наприклад, для логування, зберігання даних).

Пишіть код так, щоб його можна було легко протестувати

Одна з ознак якісного ООП-коду — тестованість.

  • Не робіть методи, у яких усе зав’язано на глобальні змінні чи статичні поля.
  • Не бійтеся впроваджувати залежності — через параметри конструктора (Dependency Injection).
  • Розділяйте обчислення (логіку) та взаємодію з користувачем (введення/виведення). Це полегшить не лише тестування, а й доопрацювання застосунку.

Корисні прийоми та антипатерни для початківців

  • Не пишіть «клас Бога» (God Object), який робить усе.
  • Не робіть «магічних чисел» без пояснень (if (status == 42) — чому 42?).
  • Не зловживайте наслідуванням заради наслідування — іноді краще використати композицію (клас із полями інших класів, а не нащадок).
  • Не пишіть складні методи довжиною у 100 рядків — їх важко протестувати й зрозуміти.
  • Завжди залишайте простір для розширення (open/closed principle).

8. Як виглядає поганий і гарний код

Поганий приклад:


class s // Погано: імʼя класу з маленької літери.
{
    public int a; // немає сенсу, погане імʼя
    public void m() // Погано: метод з імʼям з однієї літери.
    {
        Console.WriteLine(a);
        // Погано: незрозуміло, що робить метод.
    }
}

Гарний приклад:


// Представляє студента.
public class Student
{
    // Вік студента.    
    private int _age;
    public int Age
    {
        get => _age;
        set => _age = value < 0 ? 0 : value;
    }

    // Виводить інформацію про студента.
    public void PrintInfo()
    {
        Console.WriteLine($"Вік студента: {Age}");
    }
}
1
Опитування
Помилки при наслідуванні, рівень 25, лекція 4
Недоступний
Помилки при наслідуванні
Поширені помилки при оголошенні класів та об'єктів
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ