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
OOPsie: Abstract Classes & Methods
After learning the basics of classes, objects, function overriding, and polymorphism, we only have a few topics left: interfaces and abstraction.
Welcome back, game developers, to another episode of OOPsie. When we started this series we talked about the basics of object-oriented programming (OOP) which included classes and objects. Afterwards, we headed into function overriding and polymorphism, or the process of relating objects through the use of an inherited structure. Next, we'll talk about ways we can modify what we learned last article to be more appropriate in the context of our class structure.
Abstract Class & Methods
One tweak to the inheritance structure we learned earlier is the concept of abstract classes and abstract methods. Let's start with abstract classes. Abstract classes are class definitions that cannot be instantiated. That is, no objects can be created of that class type. These are useful in cases where we need to promote inheritance but it might not make sense to create an object of that type. Usually this applies exclusively to classes that don't mimic something concretely defineable in real-life. Let's look at an example:
Unreal Tournament 3
Above are four weapons from Unreal Tournament 3. Clockwise from the upper left we have the rocket launcher, longbow AVRIL, sniper rifle, and bio rifle. For the purposes of our example, we'll focus on the RocketLauncher and the SniperRifle since those are the most common and easy to remember. Ok, now we'll build a simple inherited structure using a base class of Weapon and two subclasses, RocketLauncher and SniperRifle.
public class Weapon
{
public Weapon()
{
}
public virtual String Fire()
{
return "Bam!";
}
}
public class RocketLauncher : Weapon
{
public RocketLauncher()
{
}
public override String Fire()
{
return "Boom!";
}
}
public class SniperRifle : Weapon
{
public SniperRifle()
{
}
public override String Fire()
{
return "Ping!";
}
}
public class UnrealGame
{
static void Main(string[] args)
{
//Create a list of weapons
List weapons = new List();
//Add some weapons
weapons.Add(new RocketLauncher());
weapons.Add(new SniperRifle());
//Wait, this doesn't make sense!
weapons.Add(new Weapon());
foreach(Weapon weap in weapons)
{
Console.WriteLine(weap.Fire());
}
}
}
OUTPUT:
Boom!
Ping!
Bam!
Uh-oh, while the majority of our code worked fine, something very strange is showing up. Our weapons list now has a Rocket Launcher, a Sniper Rifle, and a Weapon... weapon. In the real-world, 'weapon' is a non-concrete idea that can define a whole group of items.
We can fix this with the abstract keyword by making the Weapon class abstract. But before we see the updated code, we should introduce abstract methods. Abstract methods are method signatures defined in a base class that force inheriting classes to provide their own implementations. This is really easy to understand when using our weapon example. Since every weapon has a different style of firing, there's no reason for us to provide a base implementation (in our case, the string 'Bam!'). What we want to do is force other classes that inherit from Weapon to create their own version of Fire().
One other neat thing to notice, a class with an abstract method forces all subclasses to have an implementation but the base class doesn't. For this reason, we cannot create an instance of the base class and it must be abstract. Thus, every class with an abstract method must be abstract itself.
Let's update our weapons example by making Weapon an abstract class and making Fire() an abstract method...
public abstract class Weapon
{
public abstract String Fire();
}
public class RocketLauncher : Weapon
{
public RocketLauncher()
{
}
public override String Fire()
{
return "Boom!";
}
}
public class SniperRifle : Weapon
{
public SniperRifle()
{
}
public override String Fire()
{
return "Ping!";
}
}
public class UnrealGame
{
static void Main(string[] args)
{
//Create a list of weapons
List weapons = new List();
//Add some weapons
weapons.Add(new RocketLauncher());
weapons.Add(new SniperRifle());
//Now this line will generate a compile error!!
//weapons.Add(new Weapon());
foreach(Weapon weap in weapons)
{
Console.WriteLine(weap.Fire());
}
}
}
OUTPUT:
Boom!
Ping!
There are 3 major additions to the above code. First, the Weapon class definition has been modified to include the abstract keyword which is required as it has abstract members. Second, the Fire() function in the Weapon class has been modified to also include the abstract keyword, but more importantly the implementation we had in that class (which printed 'Bam!') has been removed. Remember, abstract methods force inherited classes to write their own implementations so it makes no sense that an abstract method would specify its own implementation. Essentially, an abstract method says, "My class cannot be instantiated (created) and my children need to define their own implementations of me!"
The final major update is in the Main() method where we have been forced to remove the line that created a new gun of type Weapon. Remember, the abstract class keyword mentioned above tells the compiler that no one is allowed to create an object of type Weapon without going through a subclass. In fact, if you were to uncomment that line you should receive a compile/build time error for this reason.
Interfaces
An interface is a classification to something we've already seen in the previous sample. An interface is class that only contains abstract members. Sometimes I tend to lean towards calling a class fully abstract when talking about an interface which re-states the definition of interface.
Now that you know the definition, hopefully you can see the interface we unintentionally created previously. The Weapon class only contains a single method signature and since it's abstract, the class is an interface (i.e. it is fully abstract). We can modify the code one last time and update Weapon to use the interface keyword:
public interface Weapon
{
public abstract String Fire();
}
public class RocketLauncher : Weapon
{
public RocketLauncher()
{
}
public override String Fire()
{
return "Boom!";
}
}
public class SniperRifle : Weapon
{
public SniperRifle()
{
}
public override String Fire()
{
return "Ping!";
}
}
public class UnrealGame
{
static void Main(string[] args)
{
//Create a list of weapons
List weapons = new List();
//Add some weapons
weapons.Add(new RocketLauncher());
weapons.Add(new SniperRifle());
foreach(Weapon weap in weapons)
{
Console.WriteLine(weap.Fire());
}
}
}
OUTPUT:
Boom!
Ping!
Notice, the Main() method of the program stays the same including the list of weapons, thus showing us that even though Weapon is defined as an interface, it can be used polymorphically. We've successfully modified our program so the programmers that use the Weapon class cannot create a gun of type Weapon but can promote inheritance by creating whatever unique type of weapons they want and simply inherit the shared function definitions of the Weapon class while allowing that class to retain it's class individuality (which we spoke about in the last OOPsie).
Conclusion
It's been a long road through our OO lessons but I've finally discussed everything I set out to discuss with the first OOPsie. We travelled through objects, classes, polymorphism, method overriding, abstract methods and classes, and interfaces. It's pretty amazing how simple we began by discussing blueprints and ended up talking about polymorphism with interfaces.
We applied all these practices with real-life game development examples. We looked at the similarities and differences with classes and objects by analyzing cars in Gran Turismo. The characters in Halo Reach provided us with another example of a blueprint. We learned about individuality and function overriding with the zombies in Dead Rising. And finally, we created abstract methods and interfaces with the weapons of Unreal Tournament 3.
I think at this point you've got a great start on the world of OOP, and programming in general if you haven't already got that. Hopefully you've been able to see how practical OOP is and how prevalent it can be found in just about every game (and darn near every current piece of software). Is there more to talk about? Of course, but I'll leave that for another article.
Read more about:
Featured BlogsAbout the Author
You May Also Like