Одна из вещей, над которыми я сейчас работаю, имеет некоторое сходство с игрой. В целях иллюстрации я собираюсь объяснить свою проблему, используя пример, взятый из фиктивной гипотетической игры.
Позвольте назвать его 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.