Хорошо ли иметь запах кода, если он допускает более простое решение другой проблемы? [закрыто]

31

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

Так что именно это мы и сделали; мы создали действительно элегантную архитектуру, которая позволила нам разделить поведение маркера на различные компоненты, которые можно прикрепить к экземплярам маркера по своему желанию, что-то вроде системы компонентов Unity . Это работало хорошо, это было легко расширяемо, это было гибко и охватывало все наши базы, но была небольшая проблема.

Наше приложение также включает в себя большое количество процедурной генерации, а именно мы процедурно генерируем поведение пуль. Почему это проблема? Что ж, наше ООП-решение для представления поведения пули, хотя и элегантное, немного сложнее в работе без человека. Люди достаточно умны, чтобы думать о решениях, которые логичны и умны. Алгоритмы процедурного генерирования еще не настолько умны, и нам было трудно реализовать ИИ, который использует нашу архитектуру ООП в полной мере. Следует признать, что недостатком архитектуры является то, что она не интуитивна во всех ситуациях.

Таким образом, чтобы устранить эту проблему, мы в основном перенесли все варианты поведения, предлагаемые различными компонентами, в класс маркеров, чтобы все, что мы могли себе представить, предлагалось непосредственно в каждом экземпляре маркера, а не в других связанных компонентах. Это немного облегчает работу с нашими алгоритмами процедурного генерирования, но теперь наш класс пули - это огромный объект бога . На сегодняшний день это самый большой класс в программе, в котором кода в пять раз больше, чем всего остального. Это тоже немного сложно поддерживать.

Это нормально, что один из наших классов превратился в объект бога, просто чтобы было легче работать с другой проблемой? В целом, нормально ли иметь запах кода в вашем коде, если он допускает более простое решение другой проблемы?

user3002473
источник
1
Ну ... это довольно сложно ответить. На мой взгляд НЕТ. Особенно, если вы работаете вместе с другими людьми. Если вы работаете один, вы можете делать все, что захотите. Но в целом все зависит от того, с кем вы работаете.
Саркастический картофель
13
«Если это глупо, и это работает, это не глупо». При этом вы должны учитывать, что даже если он работает именно так, как вы того хотите, независимо от того, не делаете ли вы в будущем кодирование для этого класса богов практически невозможным из-за того, как вы решили это исправить.
Флейтер
8
Лучше чувствовать запах и знать, что ты воняешь, чем чувствовать запах только других людей. :)
Reactgular
1
Похоже, вам нужен класс адаптера, который обеспечивает не-OO интерфейс для вашего процедурного кода, так что вы можете сохранить оставшуюся часть кода в чистоте (er).
nikie
1
Похоже, вы построили замечательную систему и решили взорвать все это до чертиков, теперь она не работает, как ожидалось, для одной функции. Это действительно то, что вы хотите? Гордитесь своей работой!
Мачта

Ответы:

74

При создании реальных программ часто приходится идти на компромисс между тем, чтобы с одной стороны оставаться прагматичным, а с другой - оставаться на 100% чистым. Если сохранение в чистоте запрещает вам вовремя отправлять ваш продукт, тогда вам лучше с небольшим количеством клейкой ленты, чтобы вытащить грязную вещь из двери.

Сказал, что ваше описание звучит по-другому - кажется, вы не собираетесь добавлять немного клейкой ленты, похоже, что вы собираетесь разрушить всю свою архитектуру, потому что вы не выглядели достаточно долго и усердно для лучшего решения. Поэтому вместо того, чтобы искать кого-то здесь, в PSE, который дал бы вам благословение, вам лучше задать другой вопрос, в котором вы подробно опишите некоторые проблемы, которые у вас есть, и посмотрите, не предложит ли вам идею, которая избегает бога. подход.

Может быть, класс bullet может быть спроектирован так, чтобы быть фасадом для множества других классов, поэтому класс bullet становится меньше. Может быть, шаблон стратегии может помочь, чтобы пуля могла делегировать различные варианты поведения различным объектам стратегии. Возможно, вам нужен только адаптер между вашим компонентом пули и процедурным генератором. Но, честно говоря, не зная больше деталей вашей системы, можно только догадываться вокруг.

Док Браун
источник
6
Просто чтобы подтвердить этот ответ, я собирался написать что-то с теми же двумя рекомендациями. Что касается последнего абзаца, то, что описывает OP, похоже, сам по себе является запахом кода, указывающим на необходимость другого уровня косвенности. Будь то лучший заводской класс, фасады или даже полноценный DSL, для которого ИИ может генерировать, оставлено на усмотрение оператора.
Даниэль Б
2
Хорошие предложения по Фасаду / Стратегии для разделения кода на более мелкие куски. Хотя, не зная больше деталей, трудно понять, что предложить.
user949300
25

Интересный вопрос. Я немного предвзятый, хотя из-за моего предыдущего опыта, который побуждает меня ответить нет.

Короткий ответ: мы никогда не прекращаем учиться. Когда вы врезаетесь в такую ​​стену, это шанс улучшить ваши архитектурные / дизайнерские навыки, а не оправдание для добавления запахов кода.

Более длинная версия состоит в том, что мне уже много раз задавали подобные вопросы в моих проектах на работе, но неизбежно мы закончили тем, что обсуждали проблему гораздо глубже, чем первоначальный спрашивающий дал ей должное. Вы хотите включить в это менталитеты, такие как анализ первопричин.

Я нашел 5 Whys особенно полезными. Ваше объяснение здесь выглядит так же, как первое почему:

  • «У нас есть этот класс богов сейчас» Почему?
  • «Потому что это упрощает алгоритм процедурной генерации»

Что именно упрощается? И почему это так? Продолжайте идти по этому пути, и обычно происходит то, что вы начинаете распознавать более фундаментальные проблемы, но в то же время они также дают вам более фундаментальные решения.

Короче говоря, после более глубокого осмысления рассматриваемой проблемы и, в частности, ее причин, по моему опыту, первоначальный вопрос оказался недействительным и совершенно неинтересным для всех участников. Если у вас есть сомнения, бросьте им вызов, и либо они исчезнут, либо вы будете знать, что вам нужно делать. Имейте в виду, это хороший знак, на мой взгляд, иметь эти сомнения в отношении кода / дизайна. Другие называют это внутренним чувством, но чтобы бросить вызов этим проблемам, вы должны сначала их распознать. Так как вы сделали этот первый шаг, поздравляем! А теперь иди в кроличью нору ...

Фрэнк
источник
9

Подобный класс богов никогда не желателен, так как это не только означает, что ваши пули теперь являются монолитными объектами, но то же самое относится и к алгоритму процедурной генерации.

Первым шагом было бы проанализировать, почему именно ваш ИИ имел столько проблем со сложностью вашего паттерна?

Вы случайно не пытались превратить свой ИИ в объект класса бога, полностью осознавая семантику каждого возможного свойства? Если вы сделали, вот где проблема возникла.

Тогда решение состояло бы не в том, чтобы интегрировать все стратегии в сам класс пули, а вместо этого перенести подсказку для ИИ из ядра ИИ в сами реализации стратегий.

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

Вместо этого у вас теперь есть все проблемы, связанные с объектами богов: не только ваш класс богов трудно понять, трудно отладить монолит, но то же самое относится и ко всем другим компонентам, имеющим доступ к такому классу богов. Из-за отсутствия абстракции ваш ИИ теперь должен превратиться в мерзость аналогичной сложности, так как теперь он должен знать все избыточные, индивидуальные свойства.

Даже сейчас вы уже испытываете проблемы с техническим обслуживанием. Эти проблемы усугубляются, особенно когда вы теряете членов команды, у которых все еще есть когнитивная модель работы этого класса.

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

Ext3h
источник
Одна вещь, которая часто отсутствует в дискуссиях о том, какими большими или сложными должны быть классы, - это различие между «насколько должен код, содержащий ссылку на тип X, быть в состоянии сделать с ним», по сравнению с тем, сколько логики должно быть в файле классов для ИКС". Если у клиента, скорее всего, есть коллекции объектов, которые поддерживают различные возможности (например, «fnorble»), и часто он хочет попросить всех членов коллекции, например, «fnorble, если возможно, иначе ничего не делать», то имея интерфейс который сочетает в себе множество таких методов, может оказаться гораздо более полезным, чем попытка разбить интерфейс.
суперкат
Учитывая такой интерфейс, можно иметь код, инкапсулирующий объекты с любой комбинацией способностей, без необходимости обрабатывать разные комбинации отдельно. Разделение интерфейсов привело бы к необходимости написания множества различных прокси-классов, поскольку прокси для типа, поддерживающего метод X, нельзя использовать для переноса объектов, которые не могут X, и прокси, которые могут переносить объекты, которые не могут X, не будет не может быть в состоянии раскрыть метод X, даже если он оборачивает объект, который может его поддерживать.
суперкат
8

Весь плохой код с незапамятных времен имеет свою историю, которая заставляет его выглядеть шаг за шагом. Ваш не исключение. Кодеры учатся кодированию. Есть аспекты вашей проблемы, которые вы не могли предвидеть, которые кажутся очевидными сейчас. Есть решения, которые вы приняли, которые были вполне разумными постепенно, но в целом привели вашу архитектуру в неправильном направлении. Вам необходимо определить и пересмотреть эти решения.

Спросите у своего офиса, есть ли у людей идеи, как это исправить. В большинстве мест, где я работал, около 10-20% программистов действительно разбираются в подобных вещах и просто ждали этого момента. Выясните, кто эти люди. Часто именно ваши новые сотрудники, которые исторически не вкладывались в текущую архитектуру, могут легче всего увидеть альтернативы. Соедините их с одним из ваших ветеранов, и вы можете быть удивлены тем, что они придумали вместе.

Карл Билефельдт
источник
2

В некоторых случаях это определенно приемлемо. Тем не менее, мне трудно поверить, что не существует хорошего решения, использующего как процедурную генерацию, так и вашу хорошую архитектуру поведения, основанную на прикрепленных компонентах. Если все виды поведения, которые только что были включены в класс bullet, не имеют функциональной разницы между объектом God и аккуратной архитектурой. Что затруднило использование ваших процедурных алгоритмов генерации?

Я думаю, что новый вопрос (может быть, здесь, или на gamedev.stackexchange.com?), Где вы описываете, какие проблемы у вас возникли с вашей архитектурой в сочетании с proc. генерал., было бы действительно интересно. Дайте нам знать, если вы тоже зададите новый вопрос!

Рой Т.
источник
3
Можете ли вы привести какой-либо пример для ситуации, когда наличие класса бога было бы и приемлемым, и желательным, в то время как этот класс не просто автоматизированная компиляция отдельных источников?
Ext3h
С приемлемым я имел в виду "Можно ли иметь запах кода, если он допускает более простое решение другой проблемы?" Бог классы, как правило, легко решаемы с помощью композиции. Но да, есть определенные запахи кода, от которых определенно не стоит избавляться на 100%. В конце концов, для выполнения работы требуется некоторый прагматизм :). (Это не означает, что я думаю, что вы должны выбрасывать лучшие практики по прихоти. Я считаю, что большинству разработчиков программного обеспечения было бы лучше следовать более строгим рекомендациям).
Рой Т.
0

Для вдохновения вы, возможно, захотите взглянуть на функциональное программирование или, точнее, на узкие типы. Идея в том, что вещи не работают, невозможно представить в имеющейся у вас системе типов. Например, допустим, у вас есть пистолет с компонентами «стрелять пулями» и «держать пули». Если они представляют собой два отдельных компонента, есть конфигурации, которые не имеют смысла - у вас может быть пистолет, который стреляет пулями, но не имеет его в своем хранилище, или пистолет, который хранит пули, но не стреляет из них.

На этом уровне сложности вы думаете: «Хорошо, человек поймет, что это бесполезно, и избежит невозможных комбинаций». Лучшим способом может быть сделать это совершенно невозможным. Например, для компонента «стрелять пулями» может потребоваться ссылка на компонент «держать пули» (или просто держать его как часть самого себя, хотя это имеет свои проблемы).

Хотя ваши примеры могут быть намного сложнее, ключ по-прежнему ограничивает возможные комбинации, добавляя ограничения. В некотором смысле, если это слишком сложно для процедурной генерации, чтобы понять это правильно, это, вероятно, слишком сложно в любом случае. Подумайте о том, как вы ограничиваете себя при разработке оружия - говорите ли вы себе: «Правильно, у этого пистолета уже есть огнемет, нет смысла добавлять гранатомет»? Это то, что вы можете включить в свою эвристику. Возможно, вы захотите использовать вероятностный механизм, который немного сложнее, чем просто дать фиксированный шанс наличия каждого компонента - у вас могут быть компоненты, которые имеют тенденцию исключать друг друга, или компоненты, которые, как правило, хорошо работают вместе.

И наконец, подумайте, действительно ли это достаточно большая проблема. Это разрушает удовольствие от игры? Являются ли в результате оружие бесполезным или подавленным? Как много? Является ли один из 10 бессмысленным? Такие игры, как Borderlands (с процедурно генерируемыми эффектами оружия) часто игнорируют бессмысленные комбинации эффектов - какой смысл иметь дробовик с 16-кратным прицелом? И все же это происходит очень часто в Borderlands. Это просто игра для смеха, а не как провал механизмов генерации :)

Luaan
источник