Remember that coupling is not inherently evil. Coupling between unrelated subsystems is what you want to avoid. If it makes sense for several components to know how to interact with each other, let them do so.
I agree if I was making a game today and needed to get it done I would do this. For research though I want to explore a more strict representation of component design for some other benefits it adds that I list below.
For instance, say you have something like a CombatAnimationsController component, which listens for certain events (StartMeleeAttack, StartSpellCast, etc...) and maps those events to animations to play in the AnimatedModel component. In this case, it makes sense to assume that if the entity has a CombatAnimationsController then it also has an AnimatedModel, and so you can have the controller call the entity to obtain a direct reference to the AnimatedModel (and log an error if one isn't found) rather than going through the convolutions of handling the interaction by way of the message system. Similarly with the various components that might be involved in combat, such as Health, Buffs and Debuffs, etc...
A big part of my wanting strict decoupling is to make developing components easier and less error prone (note developing components not hooking components up as that'll be more tedious/work). Working in a completely self contained component is about as easy as you can get it. Not having any other dependent components helps a ton in separating out tasks in a group setting. Unit testing becomes so much easier to make sure the components do what they are supposed to do. I see these as huge pluses.
For research purposes I'm really forcing myself to be ignorant to the fact that hooking these messages up might be tedious, complicated, or slow performance. For right now anyway that's how I'm thinking about it.
A "goal" of mine would be to design, on paper, all the components needed for a very basic (to start with anyway) game. Then ask the community to have 1 person take 1 component and code/test it. These components would be rather small and specific and have no dependencies so it shouldn't take someone long to do this. Then once I have all the components I (game design master) would assemble them together to make the game. I really would love to see the result of such an experiement. This would be parallelism at it's best, and puts more responsibility on the designer than the coders. The designer really needs to know all interactions for everything. Maybe this is more of a technical designer vs game designer, but this would really flesh out the game ahead of time. Not saying changes won't be needed but that's another benefit. Completely isolated components will be far easier/less error prone to change (that's the idea anyway).
Your example I think fits decoupling pretty well actually (meaning doesn't seem like it poses any technical issues like the AI example I gave). Tedious? yes, but imagine you had a flowgraph or GUI for hooking these up. Designers would love that.
GameObject* zombie = new GameObject();
zombie->AddComponent(new CombatAnimationsController("StartMeleeAttack", "StartSpellCast"));
zombie->AddComponent(new AnimatedModel("melee_attack", "spell_cast"));
// not functioning code, but OnStateChanged would pass the state name to the function ChangeAnimation
zombie->GetComponent("CombatAnimationsController")->OnStateChanged.Bind(zombie->GetComponent("AnimatedModel"), AnimatedModel::ChangeAnimation);
// as a not so good but to show the point, the order of states in COmbatAnimationsController vs animation names in AnimatedModel should match so when OnStateChanged is triggered and passes 2 (for example) to AnimatedModel, it'll play the "spell_cast" animation because it was defined second. You can get more involved with start, middle, end animations and such, and make the relationship outside of the components themselves, but this just shows a basic example of how you might do that
?
So to the original question if you were faced with the challenge of making decoupled components that are hooked up via events in 1 central init place, how can you see AI interacting with each other and the player working? That's sort of the sticky point right now for me. I'm sure it can be done, but need to keep playing around with different ways but wanted to see if anyone wanted to help take the challenge here. :)
How would an AI "see" the player and then "attack" the player without knowing anything about the components hooked up to the player from within itself. It only knows about the player and it's components in the Init() method.
?