January 13, 2022

Design pattern highlights - The Template Method pattern

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
2
3
4
5
6
class Data { /* Some data*/};
class Source
{
public:
virtual Data Get() = 0;
};

From a common source we may derive and implement different sources:

DBSource:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class DBSource : public Source
{
public:
Data Get() override
{
DBConnection connection = ....

connection.Connect();

auto info = connection.Get(/* Some SQL query*/);

return info;
}
};

GRPCSource

1
2
3
4
5
6
7
8
9
10
11
class GRPCSource : public Source
{
public:
Data Get() override
{
auto service = ...

auto info = service.GetData();
return info;
}
};

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
2
3
4
5
6
7
8
9
10
11
class Source
{
public:
virtual Data Get()
{
auto file = OpenFile();
auto data = ParseData();
auto info= ProcessInfo(data);
return info;
}
};

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
2
3
4
5
6
7
8
9
10
11
12
13
class Info {};
class Source
{
public:
virtual Data Get()
{
auto data = GetData(); // Abstracted, not a file anymore
auto info = ProcessInfo(data);
return info;
}
protected:
virtual Info GetData() = 0; // We don't know what's the implementation
};

Now the implementors will need to provide how to GetData.

1
2
3
4
5
6
7
8
9
10
11
class DBSource : public Source
{
protected:
Info GetData() override
{
DBConnection connection = ....
connection.Connect();
auto info = connection.Get(/* Some SQL query*/);
return info;
}
};

Example - Designing a spell system

Different types of spells will cause different effects however we also have common behavior.

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public interface ISpell
{
void Cast();
}

public abstract class AttackSpell : ISpell
{
public void CastOn(FightEntity entity)
{
var damage = GetDamage(entity);
var spellShieldPercent = entity.SpellShieldPercent;
damage = ReduceShield(damage, spellShieldPercent);
entity.TakeDamage(damage);
}

protected abstract float GetDamage();

protected virtual float ReduceShield(float damage, float spellShieldPercent)
{
// Basic behavior
var reductionDamage = (damage * spellShieldPercent/ 100);
return damage - reductionDamage;
}
}

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
2
3
4
5
6
7
8
9
10
11
12
13
14
public class FireBallSpell : AttackSpell
{
private float mBaseDamage = 25;

protected override float GetDamage(FightEntity entity)
{
var bonus = 1.0f;
if(entity.Type == FightEntity.Nature)
{
bonus = 2.0f;
}
return mBaseDamage * bonus ;
}
}

And we can implement different bonus effects according to states:

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.

Strategy pattern

Another 2 alternatives depending on your implementation may be:

If these don’t suit you, consider other behavior patterns.


Thank you for reading!

על הפוסט

הפוסט נכתב על ידי Ilya, רישיון על ידי CC BY-NC-ND 4.0.

שתפו את הפוסט

Email Facebook Linkedin Print

קנו לי קפה

#Software#DesignPatterns