Explicit callbacks or virtual methods

| 2 Comments

I'm in a bit of a quandary. I'm writing a class that does processing on a byte stream. You poke bytes in, it does stuff, it gives you bytes out. My quandary revolves around how it gives you the bytes...

The processor operates in two directions, that is, there's an inside and an outside. It processes data traveling from inside to outside and also from outside to inside. Poking a set of bytes into the processor in one direction may or may not result in bytes being output in that direction and may or may not result in bytes being output in the other direction. My initial sketch of a design is something like:

   public:

      void Write(
         const BYTE *pData,
         size_t dataLength);

      void ReadCompleted(
         const BYTE *pData,
         size_t dataLength);

   private :

      virtual void OnWrite(
         const BYTE *pData,
         size_t dataLength) = 0;

      virtual void OnReadCompleted(
         const BYTE *pData,
         size_t dataLength) = 0;

But it could, as easilly be

   public:

      class ICallback
      {
         public :

            virtual void OnWrite(
               const BYTE *pData,
               size_t dataLength) = 0;

            virtual void OnReadCompleted(
               const BYTE *pData,
               size_t dataLength) = 0;
      };

      Processor(
         ICallback &callback)
      :   m_callback(callback) {}

      void Write(
         const BYTE *pData,
         size_t dataLength);

      void ReadCompleted(
         const BYTE *pData,
         size_t dataLength);

In the first you'd derive from the processor and implement the virtual functions; they're be called when data was ready and you'd do stuff with it. In the second you derive from the callback and pass that to your processor and it would call you back when it had data. So which is best, and why?

I've had to do something similar to this a couple of times in the recent past. Unfortunately I've done it differently each time and, on one occasion, I seem to have taken a half and half approach (one method on the callback, one as a virtual, not quite sure why, the surrounding code may tell me if I care to look). Since we can't change the callback for the life of the processor object and since we need to create a class which implements the methods in either instance I'm tempted to think it doesn't actually matter. Of course we can always convert one style to another with a shim class anyway so it's no biggie in terms of long term decisions...

I'm just curious if anyone has any views one way or the other.

2 Comments

(Hello! Pardon my lurking. :)

I agree that it makes no functional difference in this case, but I tend to prefer interface-based callbacks to the implementation-inheritance style where you override "callback" virtual methods. The reusable utility code lives in the class, and the utility's client lives in some other class that the utility calls into. It just feels cleaner to me.

Of course, my office uses COM pretty heavily, so really it may just be a case of my preferring the style that I'm used to.

Lurking is good ;)

I guess in this case you're right. Using the callback interface would mean that there is a definite distinction between the client and the processor class and I suppose it's good to make that distinction explicit so that when there are things that are actually a way that you can 'expand' the functionality of the processor you can do that via implementation inheritance and keep the two things clearly seperate.

I picked the virtual method route last night (fractionally less typing ;) ) and right now the processor has grown two more virtual methods that allow a derived class to handle in-band commands in the data stream. If you dont derive then the data stream is processed appropriately and these commands are just removed and ignored. I think it would make my intentions clearer to switch to an interface based callback for the client methods and retain the inheritance route for enhancing the processor...

Len

Leave a comment