КОП. Компонентно-орієнтований підхід
Здарова.
Чули або не чули про КОП — неважливо. Зараз розберемось
У цій статті:
- що таке КОП на людській мові
- чому це не ООП і не ECS, але трохи і того, і того
- як я це використовую на практиці
- і де цей підхід починає сос@ти, а не бути «красивим»
Без теорії заради теорії. Поїхали
Що таке КОП
КОП — Component-Oriented Programming
Або по-нашому: компонентно-орієнтований підхід
Він сидить десь між:
- ООП (бо об’єкти, інкапсуляція, життя в MonoBehaviour)
- ECS (бо розбиваємо логіку на дрібні незалежні шматки)
Тобто так, не риба не м’ясо. І саме в цьому його плюс
В чому ідея
У реальних проєктах:
- логіка постійно повторюється
- PlayerController розростається до 2к рядків
- ворог, NPC і гравець мають 70% однакового коду
І тут приходить проста думка:
А навіщо нам PlayerController, якщо гравець — це просто набір функціоналу?
Розбиваємо на компоненти:
MoverInputAttackHealth- що завгодно
Компонент = окремий функціонал, який може жити сам
Чому не ECS
Логічне питання:
Якщо вже розбивати — то чого не ECS?
Бо ECS це:
- інше мислення
- дані окремо, логіка окремо
- адаптери до MonoBehaviour
- більший оверхед, ніж вигода в малих/середніх проєктах
А тепер питання:
Нащо?
Unity і так живе на компонентах
Додав Rigidbody — отримав фізику
Ніхто не змушує тебе створювати PhysicsControllerFactory
КОП дозволяє:
- лишитись у звичному Unity API
- не ламати мозок ECS-філософією
- при цьому не писати монолітний клас на все
База термінів (запам’ятай)
Контейнер (Repository)
Обʼєкт, який:
- зберігає компоненти
- керує їх життєвим циклом
- оновлює їх
Компонент
- одна відповідальність
- мінімум логіки
- не знає про існування інших компонентів
Філософія компонента
1. Один компонент — одна відповідальність
Без «і рух, і атака, і UI, і трішки магії»
2. Plug-n-play
Компонент:
- або працює
- або не працює
- але не вимагає ще 3 інших компонентів у правильному порядку
3. Мінімум залежностей
Компоненти не повинні напряму залежати один від одного
Не GetComponent<AnotherComponent>() в кожному методі
MonoBehaviour чи ні
Можна і так, і так
Але без MonoBehaviour:
- більше контролю
- простіший інжект
- легше тестувати
- легше розширювати
Unity залишається як адаптер, а не центр всесвіту
Мій підхід
Інтерфейси компонентів
IComponent— маркерITickableComponent— UpdateIFixedTickableComponent— FixedUpdateILateTickableComponent— LateUpdateIPlayableComponent— Play / Stop / ActiveIDestroyableComponent— Dispose / OnDestroy
Компонент реалізує тільки те, що йому реально потрібно
ComponentRepository
Один клас, який:
- зберігає всі компоненти
- викликає
Tick,FixedTick,Play - дозволяє отримати компонент по типу
Це твій локальний «міні-движок»
Приклади з репозиторіїв
Тут не абстракція заради абстракції, а реальні кейси:
- Ворог, який іде до таргету і атакує
- Рух по драбині
- HealthBar
Repo:
github.com/...Sviatenko/ComponentSystem
Варіант з MonoBehaviour трішки інша реалізація, але показано як виглядають компоненти:
github.com/...AndriiSviatenko/FPS_Melee
Коли КОП НЕ підходить
Бо магії не існує
КОП буде проблемою, якщо:
- потрібна жорстка оптимізація на тисячі ентіті
- важливий чіткий data-oriented підхід
- логіка сильно залежить від порядку виконання
- проєкт переростає в «ECS, але ми соромимось»
Тоді або нормальний ECS, або чесний моноліт. Без лицемірства
Фінал
КОП — не срібна куля
Але це адекватний компроміс між:
- чистотою архітектури
- швидкістю розробки
Все
Можеш ставити прочерк над «я не знаю що таке КОП»
Пока.
3 коментарі
Додати коментар Підписатись на коментаріВідписатись від коментарів