П’ять важливих порад, як стати майстром «гавnо коду»

Дарова-дарова-дарова! Сьогодні ми продовжимо вивчати мистецтво створення гавnо коду. Якщо ти хочеш писати так, щоб твої колеги мріяли звільнитися — ти на правильному шляху. Погнали!

Пиши змінні незрозумілими і скороченими

Хороший код:

[SerializeField] private float speed;

Гавnо код:

[SerializeField] private float sp;

Нехай наступний розробник витратить пару годин, розбираючи, що означає sp!

НЕ розбивай код на окремі модулі

Хороший код:

WinController, LoseController, PauseController, UIController

Гавnо код:

GameManager

Один монолітний «бог» має керувати грою, UI, звуком і гравцем!

Хардкодь свої залежності

Хороший код:

private IDoor _door;

Гавnо код:

private Door _door;

Інтерфейси? Поліморфізм? Це занадто складно. Хардкодь типи напряму, щоб у майбутньому кожна зміна залежностей розбивала весь проєкт(Team Forest 2))))

Пиши гігантські методи

Хороший код:

private void InitializeGame()
{
        InitializeUI();
        InitializePlayer();
        InitializeEnemies();
}

Гавnо код:

private void InitializeGame()
{
        // 500 рядків коду
        Debug.Log("Starting game...");
        Player player = new Player();
        Enemy[] enemies = new Enemy[10];
        for (int i = 0; i < enemies.Length; i++) { /* створення */}
        // Ще 400 рядків
}

Довгі методи — твої друзі. Розбивати на дрібні методи? Це ж для тих, хто думає про зрозумілість.

Ігноруй архітектуру проєкту

Хороший код:

├── Controllers/
├── Models/
├── Views/

Гавnо код:

├── Scripts/
        ├── OldScripts/
                ├── new_script (1).cs
                ├── GameManager.cs
                ├── temp/

Усе в текe Scripts. Чим більше хаосу, тим краще. А Temp — це просто ідеальне місце для «важливих» скриптів!

Висновок

Не важливо, де ти зараз. СЯДЬ і ПИШИ код! Нехай він буде легендарним. Нехай він ламає команди. Нехай він виводить всіх із себе ;)

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

👍ПодобаєтьсяСподобалось8
До обраногоВ обраному0
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

1.

float speed

— а float точно хороший тип для швидкості?

2.

private Door _door;

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

private Door _door = new Door();
... оце хардкод ;-)

3.

private void InitializeGame()

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

— а float точно хороший тип для швидкості?

А чому поганий? Розкажіть

Може профдеформація трохи )
Я джавіст, зазвичай у роботі точні числа використовую, на кшалт BigDecimal.
Float не використовував жодного разу через 1 + 1 = 1.999....
Судячи по профілям, автор і ви з GameDev-а, може у вас інші правила, і для швидкості float норм.

якщо не потрібен саме флоат, то на деяких архітектурах та реалізаціях, з int может працювати на порядок скоріше

Привіт
1) Агась самий кращий, краще тільки Реактивне поле, або класик даних
і так у нас все норм з флотами) 1f + 1f= 3f;
2) Згоден
3) Малось на увазі щоб логіку я сховував у маленькі приватні методи
Мені як іншому розробнику коли пробігаєшся по іншому класі потрібно це з методу вичленити, а так буде собі маленький метод InitGame і я буду розуміти, що тут гра інцилізується

Вітаю
1)

так у нас все норм з флотами

у нас — це С#? Чи яка мова програмування в прикладах? Можна, будь ласка, якесь посилання на доку, що

1f + 1f= 2f

?

3)

я сховував у маленькі приватні методи

Це зрозуміло. А тести на них писати як?

Роберт Мартін чи Мартін Фаулер усе і так написали, там звісно трошки крітєрієв «коду із душком», вінн же «Спегетті код». Та бідьмо відвертими вже цим усім книгам багато-багато років, а ситуація як була так і є — десь існує інженерна культура, але в більшості випадків в індустрії розробки ПО (не тільки GamdeDev) по CMM — хаотичний процесс.
Тому треба видумавати технічні інструменти які збільшують рівень абстракції. Писати чистий код та вибудовоувати усі інші процесси звісно можна. Та от вже 18 річинй досвід в ІТ мені каже — що це дуже складано і дуже дорого. Там величезна кількість перепон, яка взагалі починається від менеджерських процессів в організації бізнес і управлінської технології : поставленого процесу найму, підвищення кваліфікації і т.д. І це фінансово виправдано тільки коли рівень прибутків Big Tech, чи наприклад : Activision/Blizard, Epic Games або War Gamaing і т.п. — коли десятки мільйонів доларів прибутків на одного співробітника. Інакше от таких сильно розумних і дорогих, бізнес прибирає — а натомість бере початківців дешевше в 4-5 разів. Так з того виходить Stalker, команда звалює у власну студію робити Metro 2030 — а на їх місце беруть новачків. Тим не менше така бізнес модель показала себе цілком дієздатною.

7. Пиши змінні зрозумілими і без скорочень. Так щоб вираз «а=b+c» зайняв не менше двох екранів в ширину

6. пиши маленькі методи. Так щоб замість одного методу в 30 рядків що влізе на екран = аркуш А4 було 17 маленьких методів по 3 рядки = ітого 120 рядків = екранів 4. І так щоб вони були в рандомному порядку.

Scripts/OldScripts/

Кожного разу, коли хочеться додати в назву чогось слова «Old» чи «New» — відразу бийте себе по рукам і не робіть так! Що буде, коли з’явиться щось ще новіше, чи ще старіше? Буде VeryOld чи SuperNew? Чи буде New все ще новим через рік чи через п’ять? А коли настане час видалити Old? Ці питання не мають правильних відповідей і тому слова «Old» та «New» треба взагалі викидати з іменування сутностей.

Інформаційним спонсором цього коментаря є Новий Міст в Парижі, який зараз є найстарішим мостом через Сену.

А нащо писати нормальний код на юніті, коли життєвий цикл твоєї гіперказуалки 48 годин після релізу, який сенс?

Помню, одна топовая геймдев-контора в 2015 году стартовала новые проекты под флеш. Все отлично понимали, что дни флеша сочтены, и его вообще в принципе скоро закроют. Но задача была написать игры, и получить деньги до этого чудесного момента, и они с этой задачей вполне справились. Ну а качество кода на том самом action script уже не имеет ни малейшего значения.

Прочитав. Смішно. Але за інтерфейси справді зачепило — не можу не відписатися. Скільки вже років існує C#, скільки проєктів я бачив, скільки джунів навчав і зі скількома синьйорами сперечався ... але усе одно більшість девелоперів не люблять писати інтерфейси!
Для мене очевидно що кожен клас має реалізувати хоч би один інтерфейс. Бо він його в будь — якому разі має: клас без публічних членів не має сенсу (майже!). Але більшість девелоперів чомусь вважають що інтерфейс потрібен тільки якщо є якась ієрархія чи дві різні реалізації...
Я постійно на пальцях намагаюся пояснити що у сучасному ООП інтерфейс треба писати першим! І на це є багато причин:
1) DІ. Ну справді — давно вже час визнати оператор new єретичним — як колись зневажали go to. У C# досі є goto — але використовувати його це відзнака ламера.
2) Mock. Якщо пишете код — то дуже скоро доведеться писати юніт тест (про TDD далі). Зробили інтерфейс — вже можна створити мок. Навіть якщо у вас нема класу якій інтерфейс реалізує. Зробили клас ... треба чесати репу як тепер зробити стаб.
3) TDD. У мене часто питали: як можна починати писати першим тест, якщо ще нема класу якій він тестує. А ось як: спочатку завжди має бути інтерфейс! Бо інтерфейс — це не тільки ваш «контракт», це як креслення для інженера. Він спочатку думає як має працювати його майбутній black box, що буде на вході, а що на виході. Це і є інтерфейс, а наступний тест фактично декларує потрібну поведінку.
4) Проєктування. У будь-якій архітектурі одиницею проєктування є саме інтерфейс. Компонент, мікросервіс, якась стороння система — усе це мі використовуємо через інтерфейси. Більше того: якщо при проєктуванні ми добре розуміємо як працює кожен інтерфейс — нам навіть не треба писати реалізацію! Тому що для типових інтерфейсів реалізація або вже є, або її доволі легко написати.
Знову вийшло багато слів — але це мій біль, коли я бачу цілі проєкти з сотнями класів і десятком інтерфейсів (і ті переважно для сервісів). Відповідно там або взагалі нема DI і юніт тестів, або замість цього якісь саморобні монстри: білдери фабрик і врапери стабів.
Але мені цікаво от що: якщо ШІ вже такий розумний — чи можливо навчити його робити одну просту річ: брати старий проєкт без інтерфейсів і DI, додавати інтерфейс кожному класу — а реалізацію навпаки ховати, прибирати усі new і заміняти їх на інжекшин з DI контейнера?
Якщо ШІ зможе це робити з мінімумом помилок — то наступним кроком він зможе справді залишити без роботи половину девелоперів (джунів так точно) — бо зможе покривати старий код юніт тестами автоматично!
На мій погляд нема кращої допомоги від ШІ ніж писати юніт тести — бо це у більшості випадків очевидний код, який треба просто механічно набрати для усіх варіантів.

Для мене очевидно що кожен клас має реалізувати хоч би один інтерфейс

😃🔫
🫲🔪
🛁⚡️
це маленьке пекло для мене. Треба почитати код, замість просто ctrl+click по методу — додатково шукай імплементацію (яка іноді може не підтягнутись в IDE), треба додати метод — пиши і тут і там, змінити щось — теж саме, ще й файл постійно втрачається серед купи інших файлів.

А головне — нафіга? TDD? Так а в чому проблема просто написати пусті методи? DI? Так фреймворку пофігу інтерфейс в тебе, абстрактний клас, чи просто клас. Проектування? Так проектуй на рівні пустих методів, а потім вже роби імплементацію. Моки? Мок-ліби працюють як з інтерфейсами так й з класами в більшості мов.

Але більшість девелоперів чомусь вважають що інтерфейс потрібен тільки якщо є якась ієрархія чи дві різні реалізації...

+ Коли є хоча б теоретична можливість появи іншої додаткової імплементації.
+ Dependency inversion, я не хочу щоб сервіс залежав від бази, тому робимо інтерфейс.
Тобто щоб обозначити архітектурну роль класу, хоча й немає додаткових імплементацій.

Але нафіга інтерфейс скажімо сервісу? Контролер знаходиться на рівні вичше ніж сервіси, dependency inversion не потрібен, поява додаткової імплементації не має сенсу, тут або ця бізнес логіка або інша, архітектурно це теж немає жодного сенсу — контролер викликає конкретну бізнес логіку, а не якусь там абстрактну. Тупо бойлерплейт заради бойлерплейту. Додайте ще пару шарів й в вас вже повноцінний лазанья код

+ Додати інтрфейс взагалі не проблема, якщо колись знадобиться для яких тось архітектурних рішень. Але до того як він знадобиться, якщо це взагалі станеться — цей бойлерплейт буде валятись в коді й всіх бісить.

але усе одно більшість девелоперів не люблять писати інтерфейси!

Їх 50 на 50, я принаймні постійно потрапляю на іншу сторону — інтерфейси, да побільше, бажано ще й івенти-сабскрайбери всередені одного мікросервісу, щоб навіть найталановитіший розробник без сотні дебаг брейкпоїнтів не зміг розібратись що коли викликається.

Коротше, Keep it simple, це як й TDD — найбільша користь — освітня, навчитись проектуванню «зверху-вниз». Коли в тебе ця навичка вже є, keep it simple.

Проектування? Так проектуй на рівні пустих методів, а потім вже роби імплементацію.

здебільшого то є більше «прямий путь до проектування» бо інакше у більшості усьому описаному прямо читай «ні якого проектування by design»

я перевіряв ))

але загалом так я також бачу люди вміють то є обходити просто «пишучи інтерфейси» які на справді не є інтерфейсами ну і далі пішли компінації за класіком тов. достоєвськім «всє люді нєсчастні по своєму» (к) (тм)

+ Коли є хоча б теоретична можливість появи іншої додаткової імплементації.

але ж у класу всьо дно є «інтерфейс» який призначений для зовнішніх операції зі об’єктами класу? ))

+ Додати інтрфейс взагалі не проблема

бугога ))

якщо колись знадобиться для яких тось архітектурних рішень.

ну да ну да

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

так фішка же ж у тім і є що то є «інтерфейси» узагалі різного рівнів архітектури

це як й TDD — найбільша користь — освітня, навчитись проектуванню «зверху-вниз».

де ж вона «освітня» коли «проектувати» треба кожного разу? чи як «навчився» вже не треба?

ти же ж описуєш ті варіанти де же ж «не треба» так? ))

... тдд взагалі за то щоби одразу бачити використання об’єкту бо там одразу же починається багато цікавого як то для прикладу відсутність ооп як такого як то класів об’єктів та взаємодії між ними саме як між об’єктами бо на справді там by design просто «код трохи згрупований у кучки як получилось»

чи як «навчився» вже не треба?

На проекті пишеш так як прийнято в команді. Дома пишеш так як треба для розвитку.

де ж вона «освітня» коли «проектувати» треба кожного разу?
тдд взагалі за то щоби одразу бачити використання об’єкту

Ось в тому то й прикол, що з часом формується саме такий підхід в продумуванні сценаріїв використання і взаємодії, й для цього більше не потрібно писати тести або інтерфейси. Колись навіть якесь порівняння бачив, коли команда працювала в TDD, а потім навпаки «спочатку код потім тести», й другий дав кращу продуктивність при тій ж якості. Але коли аналізували — помітили що після практики TDD, навіть після відмови від нього — продовжували думати які будуть тести до написання коду й так далі, тобто підхід TDD залишився в голові, хоч фізично тести писались потім. Оце я маю на увазі під освітньою функцією.

так і запишемо: бобер ненавидить фабрику))

Нагадує невмируще “Things to commit just before leaving your job” gist.github.com/aras-p/6224951

написати на то є юніт тести щоб вловлювали то є все і всєго дєлов ))

ЗЫ: а ну до речі то є окрема класіка імхо то є «юніт тести» які на справді ні чого не вловлюють а просто виконують певній the most optimistic сценарій який по факту завалювання просто допилюється по місцю чисто щоб не завалювався (до наступного разу)

тобто от с харошої класікі ще до великих мов програмування

#define double float

але на справді ні хто толком не переймається що там є ну подумаєш ... подумаєш що? )) що у double на справді не вистачає точності float point arithmetic щоб відобразити у собі «велике» 64-бітне ціле?

... тож ні хто на справді не перевіряє що там beyond і з якіма на справді цифрами ми тута працюємо

... а потом боінгі падають ))

web.ma.utexas.edu/...​ogast/misc/disasters.html

написати на то є юніт тести щоб вловлювали то є все і всєго дєлов ))

Їх навіть і писати не треба. Там он відразу люди ламають функціонал «if», «else», «break», «continue». У вас вже є тести, які вже впадуть, якщо в них отаке всобачити. І ці тести вже бігають на кожен коміт в репозиторій. А отже цей коміт вже не можливо вмержити в основну гілку.

Там он відразу люди ламають функціонал «if», «else», «break», «continue». У вас вже є тести, які вже впадуть, якщо в них отаке всобачити.

як я пригадую є така навіть окремо технологія «тестування (юніт) тестів» як то у коді робляться випадкові зміни геть на кшталт ціх і далі на тестах дивиться чи то відображається на тестах взагалі

База! Завжди так пишу код, і не розумію тих нубів, які щось там вигадують. Справжній скіловий програміст вміє розібрати будь-який код, головне швидше збутись проекту, і щоб він якось працював, інше — фігня. В мене в компанії вже 6 програмістів звільнилось після знайомства з моїми проектами, одразу видно, слабкі програмісти, я так і кажу піему. Та й узагалі не бачив кращого програміста ніж я, чому я маю тих вискочок слухати.

хочеться ще додати про кількість параметрів в методах. якщо менше 4х, то розробник очевидно вузько мислить. більше параметрів богу параметрів!
ну і також скороченими варто писати не тільки змінні, а ще імена класів і методів також

О, гарне доповнення.
Розкажіть, як запобігти цьому!)

Взірцем проєктування мають бути методи без параметрів, або максимум з одним. Це щоб перед їх викликом треба було встановити значення для десяти змінних-членів класу, які цей метод використовуватиме. Додаткові бали, якщо клас не має жодних інваріантів.

Скоріше ні. Я вважаю, що це іронія.

Ця думка є прикладом сатиричного опису антипаттерна проєктування, який іронізує над поганими практиками в об’єктно-орієнтованому програмуванні.

Каже ChatGPT. І додає:

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

це шото класіки

en.wikipedia.org/wiki/Currying

а також більш новодєл

en.wikipedia.org/wiki/Builder_pattern

Currying — це зовсім не про те

Ну назви класів з приставкою «Controller» також не дуже красиво

а чому? а чого?

Це те ж саме, що з Manager, якщо усе з залежностями/відповідальністю норм, то норм)

У Java, якщо клас є REST API контролером або просто web контролером, загалом обробляє HTTP запити, то суфікс тільки Controller.

Пора вводити на доу золоту малину

Підписатись на коментарі