One of the things I'm working on right now has some similarities to a game. For purposes of illustration, I'm going to explain my problem using an example drawn from a fictitious, hypothetical game.
Let's call it DeathBlaster 4: The Deathening. In DB4, you have a number of Ship
objects which periodically and randomly encounter Phenomena
as they travel. A given Phenomenon
may have zero, one, or more Effects
on a Ship
that encounters it. For example, we might have four kinds of Ships
and three kinds of 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
Additionally, Effects
may interact with each other. For example, a GreenShip
that is in both a GravityWell
and a NebulaField
may derive some kind of synergy between the generated SpeedEffect
and ShieldEffect
. In such cases, the synergistic effect is itself an Effect
-- for example, there might be a PowerLevelSynergyEffect
that results from this interaction. No information other than the set of Effects
acting on a Ship
is needed to resolve what the final result should be.
You may begin to see a problem emerging here. As a naive first approach, either every Ship
will have to know how to handle every Phenomenon
, or every Phenomenon
will have to know about every Ship
. This is obviously unacceptable, so we would like to move these responsibilities elsewhere. Clearly there's at least one external class here, perhaps a Mediator
or Visitor
of some sort.
But what's the best way to do that? The ideal solution will probably have these properties:
Ship
as it is to add a new Phenomenon
.Effects
interact with each other and is capable of managing these interactions to decide what the final result will be.I've already decided what my approach will be, I think, but I'm interested to hear what the best-design consensus is. Where would you start? What avenues would you explore?
Follow-up update: Thanks for your responses, everybody. Here's what I wound up doing. My main observation was that the number of different Effects
seems to be small relative to the number of possible Phenomena
× Ships
interactions. That is, although there are many possible combinations of interactions, the number of kinds of results of those interactions is a smaller number.
You can see that, for example, although there are 12 interaction combinations in the table, there are only five kinds of effects: modifications to speed, modifications to power, modifications to shield, invulnerability, death.
I introduced a third class, the InteractionResolver
, to determine the result of interactions. It contains a dictionary that maps Ship-Phenomenon
pairs to Effects
(which are basically a delegate that performs the effect and some metadata). Each Ship
is handed an EffectStack
corresponding to the Effects
it's experiencing when the result of computing the interaction is complete.
Ships
then use the EffectStack
to determine the actual result of the Effects
on them, by adding modifiers to their existing attributes and properties.
I like this because:
Ship
s never need to know about Phenomena
.Ship
-Phenomena
relationship is abstracted into the InteractionResolver
.InteractionResolver
. Ships only have to apply the effects as necessary.EffectProcessorStrategy
. The default might be to process all effects, but, say, a BossShip
might ignore minor effects by having a different EffectProcessorStrategy
.An interesting potential option would be to use a variant of the Visitor Pattern.
Judith Bishop and R. Nigel Horspool wrote a paper about design pattern efficiency in which they explained various variants on the classic visitor pattern using C# 3 features.
In particular, I would take a look at how they work with delegates to handle the visitor pattern. Using a list or stack of delegates could potentally give you an interesting way to handle multiple effects from multiple objects, and be much easier to extend either side of the class hierarchy (add ships or add effects) without huge breaking code changes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With