Trending
Opinion: How will Project 2025 impact game developers?
The Heritage Foundation's manifesto for the possible next administration could do great harm to many, including large portions of the game development community.
Featured Blog | This community-written post highlights the best of what the game industry has to offer. Read more like it on the Game Developer Blogs or learn how to Submit Your Own Blog Post
We learn one of the cleanest ways to create objects using a design pattern with the Factory Method pattern.
Pattern Name: Factory Method pattern
Type: Creational
The factory method pattern is an incredibly useful pattern that takes polymorphism to another level. Remember, polymorphism is the practice of creating inherited code that gets masked as it's parent object.
The Pattern
The primary purpose of the factory method pattern is to return a newly created object without specifically calling the constructor of class you want to create outside of the factory. Essentially, the factory has a method that typically harbors a switch...case statement based on some passed in parameter (usually some kind of type) and returns an object based on what was passed in. Let's look at a game example:
Red Dead Redemption
In Red Dead Redemption (RDR), the player is put into a western-themed world complete with frontier style battles and animals galore. We are going to look at three different animal types found in RDR as the basis for our factory method pattern example.
Here is an example program showing an implementation of the factory method pattern:
interface IAnimal
{
String makeNoise();
}
public class Chicken : IAnimal
{
public Chicken()
{
}
public String makeNoise()
{
return "Cluck, cluck!";
}
}
public class Horse : IAnimal
{
public Horse()
{
}
public String makeNoise()
{
return "Neigh!";
}
}
public class Snake : IAnimal
{
public Snake()
{
}
public String makeNoise()
{
return "Hissss!";
}
}
public class AnimalFactory
{
public enum Species
{
Chicken,
Horse,
Snake
}
public static IAnimal createAnimal(Species animalType)
{
switch(animalType)
{
case Species.Chicken:
return new Chicken();
case Species.Horse:
return new Horse();
case Species.Snake:
return new Snake();
}
// Typically we would throw some error here
return null;
}
}
public class RDRGame
{
static void Main(String[] args)
{
Console.WriteLine(AnimalFactory.createAnimal(Species.Horse).makeNoise());
Console.WriteLine(AnimalFactory.createAnimal(Species.Chicken).makeNoise());
Console.WriteLine(AnimalFactory.createAnimal(Species.Snake).makeNoise());
}
}
Output:
Neigh!
Cluck, cluck!
Hissss!
Thats a lot of code but the first 4 classes should be standard by now. We have an interface called IAnimal forcing any classes that implement that interface to create their own implementation of the makeNoise() method.
The biggest addition to all of this (and the core of the factory method pattern) is the AnimalFactory class we created and only holds a single static method, createAnimal(). This method accepts a single parameter which is an enum that was also created called Species and holds the three types of animals that can be created. Alternatively, if you were given access to the actual subtype of the object you can check that (however that is more specific to certain languages). Using the type you don't have to make sure the enum is constantly updated.
makeNoise() simply contains a switch...case statement checking for the different possible inputs to this method and then returns a new instance of whichever animal is appropriate for that argument. Note, typically you should throw some kind of error if the switch doesn't find a case that applies to that argument. In this case we're just returning null which would blow up on us if we added something to the enum and didn't modify the method (see limitations of the pattern in the next section).
Finally we reach our common game class (RDRGame) with the standard Main() method. Inside we simply invoke the createAnimal() method three times which returns us an IAnimal object. Through the interface and (kind of) use of polymorphism we can invoke the makeNoise() method and voila, we have 3 distinct animals making noise.
Quick Note: Alternatively we could have just looped through the Species enum and passed the iterator variable as the argument. Also, instead of an enum we could have used several other ways to define what we wanted like strings or even numbers (though not recommended).
Limitations
Thats it, the factory method pattern! It's pretty obvious how this pattern can become extremely useful when you have a structure of differing object sub-types and need to be able to create one on the fly. Plus, all the nasty code for checking what type you want to create is in one section and not littered throughout your project.
However, the factory method pattern suffers from immediate code rot as soon as a new parent type is added to the project. At this point, if we wanted to add an IHuman interface we can't use this factory and need to write a whole new one (assuming IHuman isn't inheriting from IAnimal).
Conclusion
This pattern can be arguably considered less difficult to implement but more difficult to understand than the Singleton pattern we looked at before. As with all design patterns, this one should only be used when needed; i.e. when we need to cleanly seperate out common subtypes of a given parent class.
You May Also Like