Чи добре мати запахи коду, якщо це дозволяє легше вирішити іншу проблему? [зачинено]


31

Група друзів і я працювали над проектом протягом останнього часу, і ми хотіли придумати приємний спосіб представлення OOP сценарію, характерного для нашого продукту. По суті, ми працюємо над грою кулі в стилі Touhou , і ми хотіли створити систему, де ми могли б легко представляти будь-яку можливу поведінку кулі, про яку ми могли б придумати.

Отже, саме це ми і зробили; ми створили по-справжньому вишукану архітектуру, яка дозволила нам розділити поведінку кулі на різні компоненти, які можна було приєднати до випадків кулі за бажанням, на зразок системи компонентів Unity . Це працювало чудово, було легко розширюватися, було гнучким і охоплювало всі наші бази, але виникла невелика проблема.

Наша заявка також передбачає велику кількість процедурних поколінь, а саме ми процедурно генеруємо поведінку куль. Чому це проблема? Що ж, наше рішення OOP щодо представлення поведінки кулі, хоча і елегантне, трохи складне для роботи без людини. Люди досить розумні, щоб мислити рішення проблем, які є і логічними, і розумними. Алгоритми генерації процедурних процесів поки не такі розумні, і нам було важко реалізувати AI, який використовує нашу архітектуру OOP з максимальною повнотою. Справді, недоліком архітектури є те, що вона не є інтуїтивно зрозумілою у всіх ситуаціях.

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

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


1
Ну ... на це важко відповісти. На мою думку НІ. Особливо, якщо ти працюєш разом з іншими людьми. Якщо ви працюєте поодинці, ви можете робити все, що завгодно. Але загалом це залежить від того, з ким ти працюєш.
Саркастична картопля

13
"Якщо це дурно, і працює; це не дурно". Якщо говорити, то вам доведеться врахувати, що навіть якщо він працює саме так, як ви цього хочете, чи не робите ви кодування майбутнього класу богів майже неможливим через те, як ви вирішили це виправити.
Flater

8
Краще нюхати і знати, що ти смердить, то пахне лише інші люди. :)
Реакційний

1
Здається, що вам потрібен клас адаптерів, який надає інтерфейс, що не є OO, до вашого процесуального коду, щоб ви могли залишати чистий код (er).
nikie

1
Здається, ви створили чудову систему і вирішили підірвати все до пекла, але це не працює, як очікувалося, за однією функцією. Це справді те, чого ти хочеш? Пишайтеся своєю роботою!
Маст

Відповіді:


74

Під час створення реальних програм часто виникає компроміс між тим, як залишатися прагматичними з одного боку, і залишатись на 100% чистотою з іншого. Якщо перебування в чистоті забороняє вам вчасно доставляти свій товар, тоді вам краще за допомогою трохи клейкої стрічки, щоб дістати річ d *** d з дверей.

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

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


6
Тільки щоб підтвердити цю відповідь, я збирався щось написати з тими самими двома рекомендаціями. Щодо останнього пункту, те, що описує ОП, саме по собі кодовий запах, що вказує на необхідність іншого рівня опосередкованості. Будь то кращий заводський клас, фасади чи навіть повністю роздутий DSL, який AI може генерувати, залишається на OP.
Даніель Б

2
Гарні пропозиції щодо фасаду / стратегії, щоб розділити код на менші шматки. Хоча, не знаючи більше деталей, важко знати, що запропонувати.
user949300

25

Цікаве запитання. Я трохи упереджений, хоча через мій попередній досвід, який спонукає мене відповісти ні.

Коротка відповідь: Ми ніколи не припиняємо вчитися. Якщо ви вдаритесь таким чином про стіну, це шанс покращити свої архітектурні / дизайнерські навички, а не привід додати запахи коду.

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

Я знайшов 5 Whys особливо корисними. Ваше пояснення тут звучить так само, як і перше, чому:

  • "У нас зараз цей клас богів" Чому?
  • "Тому що це спрощує алгоритм генерації процедур"

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

Коротка довга історія, після більш глибокого розгляду проблеми, про яку йдеться, і зокрема про її причини, на моєму досвіді оригінальне запитання виявилося недійсним і вкрай нецікавим для всіх учасників. Якщо у вас є сумніви, киньте їм виклик і вони або пропадуть, або ви дізнаєтеся, що вам потрібно зробити. Зауважте, на мою думку, це хороший знак, щоб мати ці сумніви щодо коду / дизайну. Інші називають це почуттям кишечника, але для того, щоб оскаржити ці проблеми, потрібно спочатку їх розпізнати. Оскільки ви зробили цей перший крок, вітаємо! Тепер спустіться в кролячу нору ...


9

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

Першим кроком було б проаналізувати, чому саме у вашого ШІ виникли стільки проблем з вирішенням складності вашого шаблону?

Ви випадково намагалися перетворити свій AI в об’єкт класу бога, повністю усвідомлюючи семантику кожного можливого властивості? Якщо ви це зробили, саме звідси виникла проблема.

Тоді рішенням було б не інтегрування всіх стратегій у сам клас кулі, а натомість вивантаження натяку на AI з ядра AI на реалізацію стратегії.

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

Натомість у вас зараз всі проблеми, пов’язані з об'єктами бога: не тільки ваш клас богів важко зрозуміти, важко налагодити моноліт, але те саме стосується і всіх інших компонентів, що мають доступ до такого класу богів. Через відсутність абстракції ваш ШІ тепер перетвориться на мерзоту подібної складності, оскільки йому належить усвідомлювати всі надмірні, індивідуальні властивості.

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

Поки кожен проект, з яким я стикався, який використовував такі класи богів або богові функції, або був повністю переписаний з нуля, або припинився, без винятку.


Одне, чого часто не вистачає в дискусіях про те, якими мають бути великі чи складні класи, - це відмінність між тим, "скільки повинен бути код, який містить посилання типу X, робити це проти", наскільки багато логіки має бути у файлі класу для X ". Якщо клієнт, швидше за все, має колекції об'єктів, які підтримують різноманітні можливості (наприклад, "fnorble"), і часто виявляється бажаючим попросити всіх членів колекції, наприклад, "fnorble, якщо можливо, інакше нічого не робити", то маючи інтерфейс який поєднує багато таких методів, може бути набагато кориснішим, ніж спроба розділити інтерфейс.
supercat

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

8

У всіх поганих кодів, починаючи від світанку часу, є історія за своєю еволюцією, завдяки чому воно виглядає розумним крок за кроком. Ваше - не виняток. Кодери навчаються кодуванням. Є аспекти вашої проблеми, які ви не могли передбачити, які зараз здаються очевидними. Є прийняті вами рішення, які поступово були абсолютно розумними, але в цілому призвели вашу архітектуру в неправильному напрямку. Вам потрібно визначити та переглянути ці рішення.

Поцікавтеся у вашому офісі, чи є у людей якісь ідеї, як це виправити. У більшості місць, де я працював, близько 10-20% програмістів справді сприймають подібні речі, і вони просто чекали шансу. З’ясуйте, хто такі люди. Часто саме ваші нові наймачі, які історично не вкладаються в сучасну архітектуру, найлегше бачать альтернативи. З’єднайте їх з одним із своїх ветеранів, і ви можете бути здивовані тим, що вони придумують разом.


2

У деяких випадках це, безумовно, прийнятно. Однак мені важко повірити, що немає хорошого рішення, використовуючи як процедурну генерацію, так і вашу приємну архітектуру поведінки на основі прикріплених компонентів. Якщо у всіх формах поведінки, де тільки що потрапили в клас кулі, немає функціональної різниці між об'єктом бога і акуратною архітектурною версією. Що ускладнило використання алгоритмів процедурного генерування?

Я думаю, що нове запитання (можливо, тут чи на сайті gamedev.stackexchange.com?), Де ви окреслюєте, які проблеми у вас були зі своєю архітектурою в поєднанні з проц. ген., було б справді цікавим. Повідомте нас тут, якщо ви теж зробите нове запитання!


3
Чи можете ви навести будь-який приклад для ситуації, коли наявність класу богів було б і прийнятним, і бажаним, поки цей клас не є лише автоматизованою компіляцією окремих джерел?
Ext3h

З прийнятним я мав на увазі "Чи добре мати запахи коду, якщо це дозволяє легше вирішити іншу проблему?" Класи Бога, як правило, легко вирішуються за допомогою композиції. Але так, є певні запахи коду, яких точно не варто завжди на 100% позбуватися. Врешті-решт, потрібен деякий прагматизм, щоб завершити роботу :). (Це не означає, що я думаю, що ви повинні кинути кращі практики на примху. Я вважаю, що більшість програмних будинків краще буде більш чітко слідувати кращим практикам).
Рой Т.

0

Для натхнення ви можете ознайомитись з функціональним програмуванням або, точніше, типово підібраними типами. Ідея полягає в тому, що речі не працюють неможливо представити у наявній системі типів. Наприклад, скажімо, у вас є пістолет, у складі якого є "стріляти кулями" та "утримувати кулі". Якщо вони є двома окремими компонентами, є конфігурації, які не мають сенсу - у вас може бути пістолет, який стріляє кулями, але його немає в складі, або пістолет, який зберігає кулі, але не стріляє з них.

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

Хоча ваші приклади можуть бути набагато складнішими, ключ все ще обмежує можливі комбінації, додаючи обмеження. Зрештою, якщо процесуальне покоління занадто складне, щоб виправитись, це, мабуть, занадто складно. Подумайте про всі способи, які ви обмежуєте, розробляючи зброю - ви скажете собі "правильно, у цієї гармати вже є вогнемет, немає сенсу додавати гранатомет"? Це те, що ви можете включити у свою евристику. Можливо, ви хочете використовувати механізм вірогідності, який трохи складніше, ніж просто давати фіксований шанс мати кожен компонент - у вас можуть бути компоненти, які, як правило, виключають один одного, або компоненти, які, як правило, добре працюють разом.

І останнє, подумайте, чи це насправді досить велика проблема. Це руйнує задоволення від гри? Чи не є марними або непосильними знаряддя результату? Скільки? Це один з 10 безглуздих? Такі ігри, як Borderlands (з процедурно згенерованими ефектами зброї) часто ігнорують безглузді комбінації ефектів - який сенс мати рушницю з 16-кратним прицілом? І все ж це трапляється дуже часто в Прикордонних районах. Це просто грає для сміху, а не вважається збоєм механізмів покоління :)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.