Highlight - Template method pattern
One of my favorite design patterns.
It’s excellent to abstract changing behavior and keep common behavior seperated.
The code reuse and cohesion are high while the coupling suffers a bit since most implementations use inheritance to implement it.
In general sense inheritance has higher coupling value than composition.
Template method pattern
A behavior pattern.
The template method pattern uses a method to abstract away a behavior and reuse a common behavior.
A good guideline to follow:
Detect changing and static behavior, and abstract the dynamic parts of the code.
This kind of reuse allows us to simplify and seperate concerns to different behavior units.
Diagram and usage
There are couple of ways to implement it depending on your language of choice.
In classes hierarchy - a common method is used while one or more methods are abstracted/virtualized.
In this example the method B
is overriden in ClassB
and ClassC
.
However methods A
and C
are the common behavior.
Common behavior and changing behavior
Behavior may change or stay static.
If we know a behavior may change depending on runtime
, configuration
or environment
we need to properly set our code for these changes.
Reading source data
We want to abstract getting data for the server’s behavior.
We need to translate Info
into Data
when returning.
1 | class Data { /* Some data*/}; |
From a common source we may derive and implement different sources:
DBSource
:
1 | class DBSource : public Source |
GRPCSource
1 | class GRPCSource : public Source |
When a behavior is static or non changing we don’t need to abstract it.
For example in the source we get data of Info
but we need to return Data
class.
This behavior doesn’t change - only the source of the Info
changes:
Source
:
1 | class Source |
But if the behavior changes we should abstract it and create a method/interface.
In case of a method - this method is a template method
.GetData
is the template method
, note that it’s not public.
1 | class Info {}; |
Now the implementors will need to provide how to GetData
.
1 | class DBSource : public Source |
Example - Designing a spell system
Different types of spells will cause different effects however we also have common behavior.
- Healing spells - would add HP to the player.
- Support spells - Add an effect or heal others.
- Attack spells - Will cause damage to the opponent.
The code will be written in C#
.
We’ll focus on the AttackSpell
.
GetDamage
is the template method
while ReduceShield
is also a template method
but take note that one is abstract
and the other is virutal
.
In this practice we can choose to implement a basic implementation or keep it unknown.
If spells decide to act differently on magic shields
they can.
For exmaple we may want to implement a powerful IgnoreShieldAttackSpell
variant.
1 | public interface ISpell |
The common behavior:entity
receives damage according to how much spell shield
it has.
The changing behavior:
Damage casted by the deriving attack spells is reduced from the entitys state.
FirBallSpell:
The FireBallSpell
will cause more damage if the entity is type of Nature
.
1 | public class FireBallSpell : AttackSpell |
And we can implement different bonus effects according to states:
- The entity uses wooden weapons.
- The entity is weak to fire damage.
- etc…
Alternatives
When behavior changes but changes too much we need to decide if we want to alternate the implementation.
The most common way to abstract
a behavior is to use an interface
,
In this sense we implement Strategy pattern
instead.
Another 2 alternatives depending on your implementation may be:
- Mediator pattern.
- Visitor pattern.
If these don’t suit you, consider other behavior patterns.
Thank you for reading!