Another Link In The Chain

Jan 04, 2012

I spend a lot of time likening myself to Hemingway makin' out with Danielle Steel, insofar as my writing prowess goes, so let's make the most of that and do up a few more words about that last blog.

Yanking Your Chain

A heaping hunk of words went into describing the "chain" mechanism of the ComponentReceivers. As a quick refresher, there is, somewhere, a chain of these receiver nodes. When a component messenger comes along, it gets passed through this chain. So, that's nice. But I think I kind of skimped on the why.

First, let's take another look at the ComponentReceiver:

template <typename COMPONENT_TYPE>
class ComponentReceiver : public BaseComponentReceiver {
private:
    typedef ComponentMessenger<COMPONENT_TYPE> SpecializedMessenger;
    COMPONENT_TYPE* m_Component;
public:
    ComponentReceiver( COMPONENT_TYPE* component ) :
        m_Component( component ) {
    }
    virtual void Accept( AbstractComponentMessenger* abs ) {
        SpecializedMessenger* messenger
            = dynamic_cast<SpecializedMessenger*>( abs );
        if ( messenger )
            messenger->Visit( m_Component );
        BaseComponentReceiver::Accept( abs );
    }
};

This guy is constructed with a reference to some Component and specialized to that Component's type with templates. It also overrides the BaseComponentReceiver's Accept method to do some cool stuff.

The so-called "cool stuff" is casting the Messenger the Receiver accepts to the type the Receiver is specialized for and, if possible, the Messenger is directed to visit the Component the Receiver references. This is a pattern we've seen before.

Now, one way or another, the specialized ComponentReceiver calls the BaseComponentReceiver's Accept so that the Messenger can be passed along the rest of the chain. And that's it.

Because the ComponentReceiver is specialized for the component types, it's the only class that derives from the BaseComponentReceiver. The BaseComponentReceiver itself only exists to build the chain and provide a common interface for a collection of Receivers.

So, when a Component needs to be made visitable, it adds a Receiver for itself to the Entity. Remember this line?

entity->GetReceiverChain()->AddReceiver(
    new ComponentReceiver<Body>( this ) );

What that says, in effect, is, "Hey, I'm a Body, let me know if anybody tries to visit me!" The ComponentReceiver is specialized for a Body component and added to the chain. If that ComponentReceiver accepts a Messenger, it can direct it to visit its Body.

By creating Receivers for themselves, Components provide a means by which Messengers can visit them. Sounds good? I'm happy enough with it, anyway.

Breaking The Chain

Knutaf posed an interesting question and one I'm trying to be more conscious about - if I wanted to change this design, could I?

The answer is, sorta.

One suggestion was being able to change the underlying data structure of the "chain" from a list to, for example, an array. This, I think, is doable. From the perspective of the Components, anyway, all they do is create a ComponentReceiver and add it to their Entity's ComponentReceiverChain. Behind the scenes, the ComponentReceiverChain could chuck the ComponentReceivers into an array or a vector or, well, whatever. Since it's responsibly for forwaring a Messenger to a Receiver, it can impose its own control structure.

As far as parallelizing, another suggestion, a lot of other things in the code base might need some work, but this wouldn't be too bad. Rework to use a structure other than the linked list and the messenger could, conceivably, be directed along all dimensions in parallel. I think. Neat.

What irks me more is the GetReceiverChain call. It exposes the inner mechanism the Entity uses to store Receivers. What if, down the road, I did want to make a different object, something like a ReceiverArray, that did use a different structure, replacing the ReceiverChain altogether? The choice of one class over the other has been spread through the codebase and reversing that decision would be a big pain.

All it would take it hiding that behind an AddReceiver method to the Entity itself.

Am I going to change it? Probably not. I think it's safe enough. However, it's an aspect of design worth thinking about going ahead. I'd like to think I'm getting better at writing code that's conducive to change, but it was eye-opening to sit down and answer whether or not I'd actually managed to do so.