Одна из вещей, над которой я сейчас работаю, имеет некоторые сходства с игрой. В целях иллюстрации я собираюсь объяснить свою проблему на примере, взятом из вымышленной, гипотетической игры.
Давайте назовем это 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
может взаимодействовать друг с другом. Например, GreenShip
, который присутствует как в GravityWell
, так и в NebulaField
, может получить некоторую синергию между сгенерированными SpeedEffect
и ShieldEffect
. В таких случаях синергетический эффект сам по себе равен Effect
- например, в результате этого взаимодействия может быть 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
для них, добавив модификаторы к их существующим атрибутам и свойствам.
Мне нравится это, потому что:
- Кораблям никогда не нужно знать о явлениях.
- Сложность отношений Корабль-Явления абстрагируется в InteractionResolver.
- Детали того, как разрешать множественные и, возможно, сложные эффекты, абстрагируются
InteractionResolver
. Корабли должны применять эффекты только по мере необходимости.
- Это включает дополнительные полезные рефакторинги. Например, способ , в котором корабль обрабатывает эффекты, может быть дифференцирован путем создания
EffectProcessorStrategy
. По умолчанию может обрабатываться все эффекты, но, скажем, BossShip
может игнорировать второстепенные эффекты при наличии другого EffectProcessorStrategy
.