Your confusion comes from the fact Eric did not say much about the actual execution model (or evaluation model) for his proposed system. He leaves this to some degree "as an exercise to the reader". In his blog, I found this sentence about it:
When the user issues a command to the system “this wizard should wield that sword”, then that command is evaluated in the context of a set of Rules, which produces a sequence of Effects.
so the question here is - what does that actually mean, and where does this kind of command evaluation will take place?
Of course, I don't know how Eric would implement this, but here is my approach for it. I would start with some kind of command interpreter, together with some rule evaluation logic inside. Players, monsters and weapons are somewhere stored in a GameStateObject
, and the set of all rules is stored in some container (maybe some kind of graph, Eric mentioned this in a reply to a comment to his blog). My idea about a command interpreter looks roughly like this:
while(true)
{
var cmd = GetNextCommandFromUser();
if(cmd.ExitGame())
break;
cmd.Execute(subsetOfRules,gameState);
}
Now imagine an Attack
command, and lets say players and monsters have a common base class Character
(or maybe there is only a Character
class with no derivations). The Attack
command will have at least two attributes AttackingCharacter
and AttackedCharacter
. Then, a potential implementation of the Execute
method inside an AttackCommand
could look like this:
virtual void Execute(rules, gameState)
{
var attackRules = rules
.Where(r => r.IsApplicableForAttacks(AttackingCharacter,AttackedCharacter));
if(attackRules.Count()==0)
{
// maybe update some stats in gameState ...
return;
}
var damage = CalculateTotalDamage(attackRules);
var costs = CalculateCosts(attackRules);
AttackingCharacter.ReduceStrength(costs);
AttackedCharacter.IncreaseDamage(damage);
// do additional things to gameState
}
(Of course, this is pretty simplified for the sake of this example, but I guess you get the idea.)
Now let us come to your core question: where is the "multi-dispatch" complexity?
Parts of it is already handled in IsApplicableForAttacks
: if the two characters are a Wizard and a Vampire, for example, this filter will make sure the remaining rules will only fit to this specific combination of characters (or at least to all combinations of players and monsters).
Note Eric Lippert might have had a different implementation in mind, with a rule being a plain data object, maybe reprented by some DSL, and a some kind of generic rule interpreter, but let us keep things simple here.
Taking a closer look into CalculateTotalDamage
, there should exist a rule which contains the amount of damage for the given combination of attacker and attacked character (and maybe there is rule which applies to whole character classes, or combinations of them, or additional rules which increase or decrease the damage under certain conditions in the game state.) And if there is no such specific rule, maybe there is a default rule which applies to all remaining cases.
Hence CalculateTotalDamage
will have to
- filter the rules if they contribute to the damage
- sort and/or prioritize them, and
- sum up or combine the damage fractions of the applicable rules
And that's where the complexity goes: partially into the rules, partially into the commands, and partially into their interaction.