Одна из вещей, над которыми я сейчас работаю, имеет некоторое сходство с игрой. В целях иллюстрации я собираюсь объяснить свою проблему, используя пример, взятый из фиктивной гипотетической игры.
Позвольте назвать его DeathBlaster 4: Deathening. В DB4 у вас есть несколько объектов Ship
, которые периодически и случайно сталкиваются с Phenomena
по мере их перемещения. Данный Phenomenon
может иметь ноль, один или более Effects
на Ship
, который встречает его. Например, у нас могут быть четыре типа Ships
и три типа Phenomena
.
Phenomena ========================================== Ships GravityWell BlackHole NebulaField ------------ ------------------------------------------ RedShip +20% speed -50% power -50% shield BlueShip no effect invulnerable death Effects of Various GreenShip -20% speed death +50% shield Phenomena on Ships YellowShip death +50% power no effect
Кроме того, Effects
может взаимодействовать друг с другом. Например, a GreenShip
, который находится как в GravityWell
, так и в NebulaField
, может получить некоторый синергетический эффект между генерируемыми SpeedEffect
и ShieldEffect
. В таких случаях синергетический эффект сам по себе является Effect
- например, из этого взаимодействия может возникнуть a PowerLevelSynergyEffect
. Для разрешения конечного результата не требуется никакой информации, кроме набора Effects
, действующего на Ship
.
Вы можете начать видеть проблему, возникающую здесь. Как наивный первый подход, либо каждый Ship
должен знать, как обращаться с каждым Phenomenon
, либо каждый Phenomenon
должен знать о каждом Ship
. Это явно неприемлемо, поэтому мы хотели бы перенести эти обязанности в другое место. Ясно, что здесь, по крайней мере, один внешний класс, возможно, Mediator
или Visitor
.
Но какой лучший способ сделать это? Идеальное решение, вероятно, будет иметь следующие свойства:
- Так же просто добавить новый
Ship
, чтобы добавить новыйPhenomenon
. - Взаимодействия, которые не дают эффекта, являются значениями по умолчанию и не требуют дополнительного кода для представления. Конфигурация по протоколу.
- Понимает, как
Effects
взаимодействуют друг с другом и способны управлять этими взаимодействиями, чтобы решить, каков будет конечный результат.
Я уже решил, каким будет мой подход, но я заинтересован в том, чтобы узнать, что такое лучший дизайн. С чего бы вы начали? Какие пути вы могли бы изучить?
Последующее обновление: Спасибо за ваши ответы, всем. Вот что я сделал. Мое основное наблюдение заключалось в том, что количество различных Effects
кажется малым по сравнению с числом возможных Phenomena
& times; Ships
взаимодействия. То есть, хотя существует много возможных комбинаций взаимодействий, число видов результатов этих взаимодействий меньше.
Вы можете видеть, что, например, хотя в таблице есть 12 комбинаций взаимодействия, существует только пять видов эффектов: изменения скорости, модификации мощности, модификации щита, неуязвимость, смерть.
Я представил третий класс, InteractionResolver
, чтобы определить результат взаимодействия. Он содержит словарь, который сопоставляет пары Ship-Phenomenon
с Effects
(которые в основном представляют собой делегат, который выполняет эффект и некоторые метаданные). Каждому Ship
передается EffectStack
, соответствующему Effects
, который он испытывает, когда результат вычисления взаимодействия завершен.
Ships
затем используйте EffectStack
, чтобы определить фактический результат Effects
на них, добавив модификаторы к их существующим атрибутам и свойствам.
Мне это нравится, потому что:
- Корабли никогда не должны знать о явлениях.
- Сложность отношений Ship-Phenomena абстрагируется с InteractionResolver.
- Подробности о том, как разрешать множественные и, возможно, сложные эффекты, отбрасываются
InteractionResolver
. Корабли должны применять только при необходимости. - Это позволяет использовать дополнительные полезные рефакторинги. Например, способ обработки эффектов корабля можно отличить, создав
EffectProcessorStrategy
. По умолчанию может быть обработано все эффекты, но, скажем,BossShip
может игнорировать незначительные эффекты с помощью другогоEffectProcessorStrategy
.