Skip navigation

Funky Inheritance

Or functor-based inheritance

Funky inheritance is my name for when you allow attachment of functors to specify behavior instead of always just subclassing.

This came up recently in some UI event handling code that I was writing. Buttons come and go, dialogs appear and get dismissed. Each dialog dismissal button (okButton) needs something specific to happen, but that same functionality is extremely unlikely to be reused in another dialog.

Normally, to write an object type that has customizable behavior (by a subclass), you would write a base class like this:

class Dialog
{
  virtual void onDismiss() = 0 ;
};

So, when you subclass Dialog, you override onDismiss to provide custom behavior that occurs when the dialog is dismissed (assume onDismiss would be invoked by a window manager).

class FileOpenDialog : public Dialog
{
  virtual void onDismiss()
  {
    // stuff to do when the FileOpenDialog is dismissed..
  }
};

So, that’s the regular (classical) inheritance pattern. Every time you want different functionality onDismiss(), you’d write a new subclass of Dialog. Another way to get custom code to run onDismiss is to allow onDismiss() to accept a functor that can be invoked …

class FileOpenDialog : public Dialog
{
  virtual void onDismiss( function <void ()> runThisAfter )
  {
    // stuff to do when the FileOpenDialog is dismissed..
    // then..
    runThisAfter() ;
  }
};

OR

You could write onDismiss as a functor in base class Dialog instead of using classical inheritance here:

class Dialog
{
  function< void (Dialog* that) > onDismiss ;
};

Now onDismiss is a functor (that may or may not be set).

So then customized behavior is by instance, not by class. Creating a FileOpenDialog object would then be like:

Dialog fileOpenDialog ;
fileOpenDialog.onDismiss = [](Dialog* that) {
  // Do something with `that`, which acts as `this`
  // if this were a member function ;)
} ;

When the window object handler finds out a dialog has been dismissed, it invokes the onDismiss handler if it has been set.

// It is detected that fileOpenDialog needs to be closed:
if( fileOpenDialog.onDismiss )
  fileOpenDialog.onDismiss( fileOpenDialog ) ;

// Above line manually passes `this`.
// If I am not mistaken, this is considered "Pythonic" (just kidding,
// but Python does require passing of self in a similarly visible fashion)

This may come across to you as very JavaScripty- or follow a prototypal inheritance pattern. But it actually isn’t prototypal inheritance, because objects do not inherit from other objects. Each instance of Dialog has its own behavior, that is not repeated by any other instance of Dialog.

Of course, this would need some more detail and fleshing out to really work. But this is the jist of funky inheritance. Regular inheritance is class-wide customization. Funky inheritance is instance-wide customization. Funky inheritance is great for when you need to define custom behavior (often one-off, for a button or a widget) without having to define an entire subclass.

Drawbacks of Funky Inheritance

Functors are unable to persist their own internal running state except as static variables. However statics inside a functor are inaccessible from outside the functor. This means modification of a functor’s internal values is difficult/impossible from outside the functor. So, that kind of sucks sometimes.

One way you can modify a functor’s internal state variables is to capture a pointer before entering the functor:

Object * someObject = new Object() ;
functor = [ someObject ]() {
  // now if someObject is modified outside the functor,
  // I will use "the most up to date" version of someObject.
} ;

Functors seem to be best used to send global state messages or somehow modify global state. For example, if a FileOpenDialog is modal, it may flip off the `modal` flag inside your window manager when it is dismissed. Functors are also useful to modify the object running the functor’s state as well.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: