Practical Examples in a Music Theme
Enter base classes, virtual methods, and override functionality. I’ve been using this OOP design pattern quite a bit at work. These patterns are a fundamental layer in OOP and can help extend and make code very reusable (by eliminating unnecessary copy/paste!). As always, I wanted to explore these concepts through a verrrry simple music-themed lens. Let’s dive in.
Base Classes and Inheritance
So, a base class in C# defines shared properties and behaviors that derived classes inherit. Very straightforward and a good example is something like an Instrument
class. An instrument can have countless variations and complexities, but will always be an instrument.
Example: The Instrument
Base Class
Here’s a base class for instruments:
public class Instrument
{
public string Name { get; set; }
public int Volume { get; set; }
public Instrument(string name, int volume)
{
Name = name;
Volume = volume;
}
public virtual void Play()
{
Console.WriteLine($"{Name} produces sound at volume {Volume}.");
}
}
The Instrument
class includes Name
and Volume
properties and a virtual
Play
method (more on that in a second), which derived classes can override. This base class serves as a template for all instruments, keeping it consistent across all derived types, like guitars, drums, ukuleles, vuvuzelas, bassons, accordions, etc, etc, etc!
Virtual Methods and Override Functionality
So what does the virtual
keyword in a base class mean? It marks a method as overridable! And that lets you override
that specific method in a derived class. Boom! Surprise Polymorphism!
Example: Overriding with Guitar
Consider a Guitar
class that inherits from Instrument
:
public class Guitar : Instrument
{
public int NumberOfStrings { get; set; }
public Guitar(string name, int volume, int numberOfStrings)
: base(name, volume)
{
NumberOfStrings = numberOfStrings;
}
public override void Play()
{
Console.WriteLine($"{Name}, a {NumberOfStrings}-string guitar, plays a melody at volume {Volume}.");
}
}
As you can see, the Guitar
class overrides the Play
method to provide guitar-specific information, incorporating the number of strings.
Example: Overriding with Drums
Similarly, a Drums
class can override Play
:
public class Drums : Instrument
{
public int NumberOfDrums { get; set; }
public Drums(string name, int volume, int numberOfDrums)
: base(name, volume)
{
NumberOfDrums = numberOfDrums;
}
public override void Play()
{
Console.WriteLine($"{Name}, a {NumberOfDrums}-piece drum kit, plays a rhythm at volume {Volume}.");
}
}
Using the Base Keyword
Perhaps you’ve noticed, in the constructor of our derived Guitar
and Drums
classes, we aren't explicitly calling the Instrument
class in the chain. That’s because we can use the base
keyword! This lets us call the base class’s constructor (or methods), properly initializing and providing the derived class access to the virtual methods.
Example: Constructor Chaining
Here it is again for the folks in the back. In the Guitar
class, the constructor uses base
to initialize inherited members:
public Guitar(string name, int volume, int numberOfStrings)
: base(name, volume)
{
NumberOfStrings = numberOfStrings;
}
This ensures Name
and Volume
are set by the Instrument
constructor before NumberOfStrings
is initialized.
Band practice below!
Here’s how the classes work together:
class Program
{
static void Main()
{
Instrument guitar = new Guitar("Stratocaster", 8, 6);
Instrument drums = new Drums("Pearl Kit", 9, 5);
guitar.Play();
drums.Play();
}
}
Output
Stratocaster, a 6-string guitar, plays a melody at volume 8.
Pearl Kit, a 5-piece drum kit, plays a rhythm at volume 9.
Breakdown (in conclusion)
- Base Class:
Instrument
provides a common structure for properties and methods. - Virtual and Override: The
virtual
Play
method allows derived classes to customize behavior, enabling polymorphism. - Base Keyword: Ensures proper initialization of inherited members, maintaining consistency.
This design pattern is available in most major OOP languages (if you’ve used Rails, you’ve seen it like this DerivedController > ApplicationController
) and something I just wanted to take a moment to reflect on. Up next, Access Modifiers and Abstraction. See you there!