The Subclass Contract
Inheritance gets a bad reputation. Much of that reputation is deserved. But there is one pattern where it genuinely shines: the template method.
The Setup
You have a base class that handles the boring parts — window management, input polling, camera math, timing loops. It exposes a set of virtual methods: draw_grid, on_tick, update. Your subclass overrides those methods. The base class calls them at the right time, in the right context.
This is the template method pattern, and it works because it inverts the usual problem with inheritance. Instead of the subclass knowing about the parent’s internals, the parent defines the contract: “I will call draw_grid while the camera transform is active. I will call draw_ui in screen space. I will call on_tick every N seconds of game time.”
The Contract
The key word is contract. Not just “you can override this method.” A contract means:
- When will this method be called? (Every frame? On a timer? On an event?)
- In what context? (Is the camera transform active? Are we in screen space? Is the delta already scaled by game speed?)
- What has already happened? (Has input been polled? Has the economy been updated?)
- What is expected back? (Just side effects? A return value? State mutation?)
Without these answers, a virtual method is just a function pointer with wishful thinking.
Why It Works in C
In GObject-based C, this pattern is explicit in a way that higher-level languages make implicit. The class struct literally contains function pointers:
struct _TemplateClass
{
ParentClass parent_class;
void (*draw_grid) (Template *self);
void (*on_economy_tick) (Template *self);
void (*update_economy) (Template *self, gdouble delta);
};
You can see exactly what the contract is. The function signatures tell you the parameters. The class_init tells you the defaults. The parent’s implementation tells you the calling order.
There is no magic. No super keyword hiding a chain of calls. You explicitly cast and call the parent class if you want to chain up:
PARENT_CLASS (my_parent_class)->on_tick (self);
The Failure Mode
The pattern fails when the contract is undocumented. When draw_grid is called but you don’t know whether you’re in world space or screen space. When update_economy receives a delta but you don’t know if it’s raw frame time or time-scaled. When the base class handles certain key presses but your subclass also wants to handle the same keys.
These are not theoretical problems. They are Tuesday.
The Lesson
If you design a derivable base class, document the contract for every virtual method. Not just what it does — when it’s called, what state exists at that moment, and what the caller expects afterward.
The implementation is the easy part. Understanding the contract is where the real work happens.