Українська
Шаблонний метод і перевірка ієрархії
Шаблонний метод та композиція
Абстрактний клас може задавати сталий алгоритм у final-методі, а окремі кроки залишати абстрактними. Наприклад, звіт завжди формує заголовок, тіло та підсумок, але підтип визначає тіло. Такий підхід називають шаблонним методом. Він відрізняється від шаблонів рядків і від узагальнених типів наступних тем.
Композиція представляє зв’язок «має». Замість наслідування Printer від File можна передати йому Formatter і Writer. Залежності мають невеликі інтерфейси, тому формат і спосіб виведення змінюються незалежно. Не кожен повторюваний рядок коду є причиною створювати спільного предка.
Принцип підстановки Лісков вимагає, щоб клієнт базового контракту залишався правильним для підтипу. Перевірте однакові сценарії для всіх реалізацій: допустиме введення, межу, відмову та очікуваний результат. Якщо підтип постійно потребує особливих винятків у клієнті, перегляньте контракт.
fun interface має один абстрактний метод і підтримує зручне створення через лямбду. У цій темі достатньо розуміти його як маленький контракт дії. Повні приклади функцій як значень і лямбд належать темі 11. https://kotlinlang.org/docs/fun-interfaces.html.
Знімок екрана
Create incomplete Shape subclass; Alt+Enter > Implement members, select area and perimeter.
Рис. 6.6. Реалізація абстрактних членів класу
UML і перевірка ієрархії
У UML наслідування позначають суцільною лінією з порожнім трикутником до базового типу. Реалізацію інтерфейсу – пунктирною лінією з таким самим трикутником. Абстрактний тип позначають курсивом або явною приміткою. Стрілка йде від конкретного типу до загального, а не навпаки.
На схемі залишайте ті властивості й методи, що пояснюють контракт. Поруч із композицією вкажіть залежність і кількість об’єктів, якщо це важливо для моделі. Розділяйте схему структури типів і схему конкретних екземплярів: це відповіді на різні запитання.
Знімок екрана
Cursor on Shape; Navigate > Type Hierarchy (Ctrl+H), expand Rectangle to Square.
Рис. 6.7. Перегляд ієрархії фігур
Для перевірки поліморфізму поставте точку зупинки в базовому клієнтському циклі та в двох перевизначеннях. Подивіться статичний тип змінної й фактичний клас об’єкта. Далі виконайте крок у метод і поясніть, чому IDE відкрила саме це тіло. Порівняйте результат із прямим викликом через змінну підтипу.
Типові помилки: забутий override, зайвий open, звуження видимості, дублювання поля базового класу, виклик відкритого методу до готовності підкласу, when із десятками перевірок типу та equals без узгодженого hashCode. Кожна має конкретне виправлення: точний контракт і менша кількість залежностей.
Проєктування контрактного тесту
Перевірка однієї конкретної реалізації й перевірка спільного контракту відповідають на різні запитання. Для Circle можна перевірити формулу площі, а для будь-якого Shape – скінченність і невід’ємність результату, повторюваність читання та відсутність зміни розмірів. Спільні перевірки запускають для кожного нового підтипу. Якщо їх неможливо сформулювати, назва базового типу ще не стала достатнім контрактом.
Таблиця 6.1. Перевірка обіцянок ієрархії фігур
| Обіцянка | Перевірка |
|---|---|
| Площа допустимої фігури додатна | Створити кожну реалізацію і перевірити результат |
| Запит площі не змінює стан | Повторити запит; порівняти площу й периметр |
| Неправильні розміри відхиляються | Спробувати нуль, від’ємне число, NaN і нескінченність |
| Клієнт не залежить від конкретного типу | Додати новий підтип без зміни циклу звіту |
Не вимагайте однакових числових результатів від різних формул, якщо контракт цього не обіцяє. Bus і Taxi мають різну ціну, проте однаково трактують допустимість відстані та нульову поїздку. Саме ці спільні властивості дозволяють коректно використати будь-який Transport у калькуляторі.
Підтип може забезпечити сильніший результат, наприклад повертати тільки ціле значення замість довільного невід’ємного, якщо клієнт базового типу від цього не страждає. Але він не повинен вимагати від клієнта додаткової підготовки, якої немає в базовому контракті. Особливо уважно перевіряйте винятки, порядок дій і доступність методів після відмови.
Якщо новий «підтип» потребує виклику prepare перед кожним area, це нова передумова. Замість приховування її в документації перегляньте модель: створюйте готовий Shape фабрикою або виділіть інший контракт для обчислення, що потребує ресурсів. Слова в UML не скасовують поведінкових відмінностей.
Під час рев’ю поясніть кожну стрілку наслідування одним реченням про підстановку. Для зв’язку «має» поясніть, хто створює залежність і чи може вона існувати окремо. Ці прості питання виявляють глибокі ієрархії, що насправді виникли лише з бажання повторно використати кілька рядків коду.