So you’re working on a video game, and you’re designing a hierarchy for characters in the game. Your game being of the slash-and-burn variety, it’s not uncommon for characters to be injured or otherwise in a reduced state of health. You therefore decide to offer a member function, healthValue
, that returns an integer indicating how healthy the character is. Because different characters may calculate their health in different ways, declaring healthValue
virtual seems the obvious way to design things:
1 | class GameCharacter { |
This is the most common design, but not the only one. What’s alternative to this approach?
Non-virtual Interface (NVI) Idiom (also called Template Method Pattern)
1 | class GameCharacter { |
You may wonder first of all how can derived doHealthValue
be called in base class? You need to understand access level is a compile-time concept. The runtime doesn’t know if a method was declared private
or public
. Calling derived doHealthValue
in base class is totally valid because it’s called through runtime dynamic dispatching.
Note that healthValue
is non-virtual in base class, and actual interface doHealthValue
is private. You can read details of NIV here
THE STRATEGY PATTERN VIA FUNCTION POINTERS
You can argue that calculate health value is independent of characters, so it should not belong to GameCharacter
class. GameCharacter
class can take in a function pointer to calculate it’s health value. This is strategy pattern.
1 | class GameCharacter; // forward declaration |
The beauty of this design is that it allows you to associate different health calculation method to the same class instances.
1 | class EvilBadGuy: public GameCharacter { |
THE STRATEGY PATTERN VIA STD::FUNCTION
With slightly change from previous example, we can make the design much more flexible in terms of what form should health calculation function conform
1 | class GameCharacter; |
This way, GameCharacter
can support much more rich way of defining health calculation function. In fact, it doesn’t have to be a function at all.
1 | // non-int return type |
THE “CLASSIC” STRATEGY PATTERN
Instead of providing an implementation of health calculation via function or callable, the classic strategy pattern is to define hierarchal structure for health calculation methods too.
1 | class GameCharacter; // forward declaration |