Українська
Проєктування та перевірка класу
Проєктування мінімального інтерфейсу
Почніть зі сценарію: «додати поїздку», «забронювати номер», «видати книгу». Потім визначте дані, які потрібні для рішення, і правила, які ніколи не можна порушувати. Для бронювання це порядок дат і відсутність перетину, для автомобіля – невід’ємне пальне та достатній запас.
Кожен публічний метод збільшує кількість способів змінити об’єкт. Універсальний setter для кожного поля часто дозволяє проміжні некоректні стани. Операція reserve(from, to) перевіряє інтервал разом; два незалежні setFrom і setTo змушують користувача вгадувати безпечний порядок викликів. Перевірку слід розташовувати біля даних.
Опис контракту має відповісти: чи дозволено порожній рядок, чи можна повторити ту саму дію, чи є верхня межа, як повідомляється помилка, чи зміниться стан у разі відмови. Ці відповіді перетворюються на тести. Конструктор із некоректними аргументами має відмовити, а не залишити «майже готовий» об’єкт із прапорцем, який легко забути перевірити.
У навчальних прикладах введення відокремлено від моделі. Клас рахунку не читає std::cin і не питає користувача повторно: цим займається консольний інтерфейс. Завдяки цьому той самий клас можна перевіряти через assert і пізніше використовувати в іншому інтерфейсі.
Діагностика й перевірка інваріантів
Недоступне поле має спричиняти помилку компіляції: це корисна межа інтерфейсу. Не виправляйте її перетворенням усіх полів на public. Спочатку з’ясуйте, яку операцію намагається виконати користувач класу, і чи вже є метод, який перевіряє її коректність.
Забутий const проявляється, коли метод читання викликають для константного об’єкта. Неправильний порядок полів може проявитися попередженням компілятора або читанням неініціалізованого значення. Розміщуйте залежні поля після тих, від яких вони залежать, і не сприймайте порядок списку конструктора як спосіб змінити правило мови.
Для рахунку перевірте нульовий баланс, точне зняття всього залишку, нульову та від’ємну суму, перевищення балансу й верхньої межі. Після кожної відхиленої операції окремо перевірте незмінність стану. Для колекції перевіряють порожній набір, повторний ідентифікатор, повне заповнення та повторне видалення вже відсутнього елемента.
Тест нормального сценарію доводить лише роботу конкретного прикладу. Він не доводить безпеку арифметики для всього діапазону int, не перевіряє час життя поверненого посилання та не підтверджує потокову безпеку. Обмеження слід записувати поруч із контрактом і свідомо розширювати лише тоді, коли з’являються відповідні перевірки.
Розбір сценарію: клас бронювання місця
Розглянемо вимогу «записати студента на курс із лімітом місць» до написання коду. Поля capacity, students і waiting самі по собі не пояснюють правила. Спочатку домовляємося: місткість додатна; номер студента унікальний у межах обох списків; кількість записаних не перевищує місткість; список очікування впорядковано за моментом запиту.
Публічний інтерфейс може складатися з enroll(id), remove(id) і report() const. Не потрібні setStudents та setWaiting: вони дозволили б підмінити колекцію без перевірки унікальності. Реалізація enroll спочатку шукає номер в обох списках. Повторний запит відхиляється, навіть якщо студент зараз лише очікує. Потім визначається місце додавання. Тільки після успішної перевірки змінюється відповідна колекція.
Для remove треба розрізняти три випадки: учасник, очікувач і невідомий номер. Видалення учасника звільняє місце й може перевести першого очікувача. Видалення очікувача не змінює кількість учасників. Невідомий номер або спричиняє явну відмову, або повертає false: обраний варіант має бути послідовним у всьому інтерфейсі.
Тепер видно, що інваріант охоплює кілька полів одночасно. Окремі перевірки «кожен vector має допустимий розмір» не виключають повторного номера у двох колекціях. Саме предметна операція може зберегти зв’язок між ними. Консольна програма лише читає команду, викликає метод і показує результат; вона не повинна вручну переносити студента між полями.
Для місткості 2 перевірочний сценарій такий: записати A, записати B, записати C в очікування, повторно спробувати A, видалити B. Підсумок: учасники A і C, очікування порожнє. Повторна спроба A не має змінити жодного списку. Цей сценарій є точнішим критерієм, ніж твердження «клас працює», бо описує і перехід, і результат, і відмову.