Introduction
The strategy design pattern is a useful pattern for pulling out frequently changing pieces of code and encapsulating them within individual classes. The strategy pattern allows us to reference these new classes in a loosely-coupled fashion, without directly referencing the concrete implementations. This gives us the powerful ability of choosing concrete classes dynamically at runtime, instead of hard-wiring them during code design. This article will show an example usage of the strategy design pattern in creating a basic RPG-style adventure game simulator. The idea for the simulator comes from the popular book Head First Design Patterns by O’Reilly.
Game Design
In this particular game design, we will have specific types of characters and weapons. This directly leads to two basic classes: Character and Weapon. All characters will inherit from an abstract Character class, which will contain the basic functions that all characters can perform, such as displaying themselves and attacking. The weapon class will contain information about the weapon and how it attacks. Since each weapon will attack differently, we will encapsulate the weapon algorithms into separate classes, through the use of the strategy design pattern.
Our First Interface IWeaponBehavior
We’re going to start the design with our first interface, which represents the design of our weapon class. This is the core piece to the strategy pattern, in that it allows us to reference the interface, rather than the concrete implementations of each weapon’s algorithm class. While you could certainly create an individual class for each weapon and include a concrete reference to that weapon in each character class, by pulling out the details of the weapon and referencing them with an interface, you keep the game design flexible, and easily modifable.
1 | public interface IWeaponBehavior |
Our interface contains two methods. One displays details about the weapon and the other executes an attack. We’ll be defining the concrete implementations of each weapon shortly. However, everything we need to begin defining the character types is here in the interface.
We Can’t Attack Without a Body
Moving on, we’ll focus on the creation of the Character class, which is the base class for all character types. Our characters need to hold a weapon and display themselves. Since we’re going to have several different types of characters, we want to utilize a base class and optimize code-reuse as much as possible.
1 | public abstract class Character |
What we have here is a character class. Notice that the class contains an instance member of the IWeaponBehavior interface. This, in effect, gives the character a weapon to hold. The details of the type of weapon or how it attacks are of no concern to the character class. The character class just knows that it needs to call Weapon.UseWeapon() when it wants to attack. The strategy design pattern will, in turn, call the appropriate concrete class which contains the details of the weapon.
Our character class also contains a public member of Weapon, to allow us to dynamically change the weapon at runtime. That is, when a character is instantiated, the constructor sets up the weapon type. This works fine, except that it sets in stone the type of weapon that this character can use. By including a means to change the weapon’s concrete class (ie. the public Weapon object), we can change the weapon whenever we wish, at run-time.
Finally, our character class contains an abstract Display() method so we can look at our character. The concrete characters will fill in the body for this method.
As far as code-reuse, our character base class contains all code for handling the weapon, which will be shared amongst all concrete character classes.
Let the Army Come Forth
Now that we have a character base class setup, using the strategy design pattern, it’s time to create some real characters. At the very least, we need a Barbarian and a Goblin. And well, what’s a kingdom without a king? Also for good measure, throw in a Paladin.
1 | public class Barbarian : Character |
Our first concrete character, Barbarian, inherits from the Character class, bringing in all the strategy design pattern code for handling a weapon. The only real meat to this class that remains to be filled in is the Display() method. Our constructor takes a generic weapon as a parameter and passes that to the base class. This is what allows us to create any type of weapon for the Barbarian. The King, Paladin, and Goblin follow the same pattern.
1 | public class King : Character |
While the concrete characters seem quite simple, the important item to note about them is the usage of the strategy design pattern in accepting an interface to a weapon as input in the constructor. This is the key to creating loosely-coupled code.
Enough Characters Already, I Want to Fight
We have enough characters to start our small kingdom, so now it’s time to give them some weapons. Since we already have an interface for our weapon, we can begin creating the concrete implementations. Each weapon will be different, but the Character class doesn’t mind. Let’s start with a Knife.
1 | public class KnifeBehavior : IWeaponBehavior |
The KnifeBehavior class implements the IWeaponBehavior interface and defines a body for the Display() and UseWeapon() functions. The strategy design pattern will dictate, at run-time, which detailed weapon class gets executed. We can define a few more weapons using the same pattern.
1 | public class SwordBehavior : IWeaponBehavior |
Putting it All Together
Now that we have our characters and weapons, we can run a quick test to see how they work with the strategy design pattern.
1 | static void Main() |
Output
1 | You are a holy paladin, slayer of evil. |
Notice in the above code, we start with a generic character variable and instantiate a Paladin from it. By passing in the SwordBehavior class, the strategy design pattern gives the Paladin a sword to use. We can now display and attack by calling the generic character’s Display() and Fight() methods. The Fight() method is particularly interesting, in that it calls the weapon interface, which in turn, calls the concrete weapon UseWeapon() method.
To demonstrate the power of the strategy design pattern in changing concrete classes at run-time, notice how we can change the Paladin’s weapon on-the-fly:
1 | static void Main() |
Output
1 | You are a holy paladin, slayer of evil. |
In the above examples, you can see how the strategy design pattern makes it easy to add new characters and weapons with no impact to the other classes.
Making the Adventure Simulator More Interactive
To really get a feel for how the strategy pattern works at run-time, we’ll provide a few menus for the user to interact with to choose a character type, choose a weapon, look at himself, and attack. We’ll also provide menu options to change the character type and weapon. Since our design uses the strategy pattern to implement the weapon behavior, we can easily interchange any type of weapon with our character class.
1 | class Program |
Output
1 | AdventureTest With the Strategy Design Pattern |
Conclusion
The strategy design pattern helps us remove frequently changing pieces of code and encapsulate them within individual classes (algorithms). By defering the decision on which type of concrete class to use, we can maintain loosely-coupled code that can dynamically change at run-time. The strategy design pattern also helps to foster code-reuse and maintainability of future code.
About the Author
This article was written by Kory Becker, software developer and architect, skilled in a range of technologies, including web application development, machine learning, artificial intelligence, and data science.
Sponsor Me