Object-oriented programming (OOP) is a cornerstone of modern software development, and understanding how to design and implement class hierarchies is crucial for building robust, maintainable, and scalable applications. This lesson focuses on applying the OOP principles you've learned to create a practical class hierarchy. By working through a real-world scenario, you'll solidify your understanding of inheritance, polymorphism, and abstraction, and gain valuable experience in designing object-oriented systems. This task will prepare you for more complex projects and lay the foundation for understanding design patterns and architectural principles in later modules.
Designing a class hierarchy involves identifying the key entities in your problem domain and organizing them into a structure that reflects their relationships. This process requires careful consideration of inheritance, abstraction, and polymorphism to create a flexible and maintainable system.
The first step in designing a class hierarchy is to identify the core entities involved in the scenario you're modeling. Consider their attributes (data) and behaviors (methods). Then, analyze the relationships between these entities. Look for common characteristics and behaviors that can be generalized into base classes, and identify specialized behaviors that can be implemented in derived classes.
For example, let's consider a scenario involving different types of vehicles. We can identify entities like Car, Truck, and Motorcycle. All of these are **Vehicle**s. A Vehicle has attributes like Make, Model, Year, and Color. It also has behaviors like StartEngine(), StopEngine(), Accelerate(), and Brake().
Inheritance allows you to create new classes (derived classes) based on existing classes (base classes). The derived classes inherit the attributes and behaviors of the base class, and can also add their own unique attributes and behaviors. This promotes code reuse and reduces redundancy.
In our vehicle example, we can create a base class called Vehicle that contains the common attributes and behaviors of all vehicles. Then, we can create derived classes like Car, Truck, and Motorcycle that inherit from the Vehicle class. Each derived class can add its own specific attributes and behaviors. For example, Car might have an attribute called NumberOfDoors, while Truck might have an attribute called CargoCapacity.
Abstraction involves hiding complex implementation details and exposing only the essential information to the user. This simplifies the user's interaction with the system and reduces the risk of errors.
In our vehicle example, we can abstract away the complex details of how the engine works and simply provide methods like StartEngine() and StopEngine(). The user doesn't need to know how the engine is started or stopped; they only need to know that they can call these methods to perform the desired actions.
Polymorphism allows objects of different classes to be treated as objects of a common type. This enables you to write code that can work with objects of different classes without knowing their specific types.
In our vehicle example, we can create an array of Vehicle objects that contains Car, Truck, and Motorcycle objects. We can then iterate through the array and call the Accelerate() method on each object. Each object will respond to the Accelerate() method in its own way, based on its specific type. This is polymorphism in action.
Now, let's translate our design into C# code. We'll start by defining the base class, Vehicle, and then create derived classes for Car, Truck, and Motorcycle.
// Base class for all vehicles
public class Vehicle
{
// Common attributes
public string Make { get; set; }
public string Model { get; set; }
public int Year { get; set; }
public string Color { get; set; }
public bool IsEngineRunning { get; set; }
// Constructor
public Vehicle(string make, string model, int year, string color)
{
Make = make;
Model = model;
Year = year;
Color = color;
IsEngineRunning = false;
}
// Common behaviors
public virtual void StartEngine()
{
IsEngineRunning = true;
Console.WriteLine("Engine started.");
}
public virtual void StopEngine()
{
IsEngineRunning = false;
Console.WriteLine("Engine stopped.");
}
public virtual void Accelerate()
{
if (IsEngineRunning)
{
Console.WriteLine("Vehicle accelerating.");
}
else
{
Console.WriteLine("Please start the engine first.");
}
}
public virtual void Brake()
{
Console.WriteLine("Vehicle braking.");
}
// Method to display vehicle information
public virtual void DisplayInfo()
{
Console.WriteLine($"Make: {Make}, Model: {Model}, Year: {Year}, Color: {Color}");
}
}
// Derived class for cars
public class Car : Vehicle
{
// Unique attribute
public int NumberOfDoors { get; set; }
// Constructor
public Car(string make, string model, int year, string color, int numberOfDoors) : base(make, model, year, color)
{
NumberOfDoors = numberOfDoors;
}
// Override method to display car information
public override void DisplayInfo()
{
base.DisplayInfo();
Console.WriteLine($"Number of Doors: {NumberOfDoors}");
}
}
// Derived class for trucks
public class Truck : Vehicle
{
// Unique attribute
public double CargoCapacity { get; set; }
// Constructor
public Truck(string make, string model, int year, string color, double cargoCapacity) : base(make, model, year, color)
{
CargoCapacity = cargoCapacity;
}
// Override method for accelerating (trucks accelerate slower)
public override void Accelerate()
{
if (IsEngineRunning)
{
Console.WriteLine("Truck accelerating (slowly).");
}
else
{
Console.WriteLine("Please start the engine first.");
}
}
// Override method to display truck information
public override void DisplayInfo()
{
base.DisplayInfo();
Console.WriteLine($"Cargo Capacity: {CargoCapacity} tons");
}
}
// Derived class for motorcycles
public class Motorcycle : Vehicle
{
// Unique attribute
public bool HasSidecar { get; set; }
// Constructor
public Motorcycle(string make, string model, int year, string color, bool hasSidecar) : base(make, model, year, color)
{
HasSidecar = hasSidecar;
}
// Override method for accelerating (motorcycles accelerate faster)
public override void Accelerate()
{
if (IsEngineRunning)
{
Console.WriteLine("Motorcycle accelerating (quickly!).");
}
else
{
Console.WriteLine("Please start the engine first.");
}
}
// Override method to display motorcycle information
public override void DisplayInfo()
{
base.DisplayInfo();
Console.WriteLine($"Has Sidecar: {HasSidecar}");
}
}
public class Example
{
public static void Main(string[] args)
{
// Create instances of different vehicle types
Car myCar = new Car("Toyota", "Camry", 2023, "Silver", 4);
Truck myTruck = new Truck("Ford", "F-150", 2022, "Black", 2.5);
Motorcycle myMotorcycle = new Motorcycle("Harley-Davidson", "Sportster", 2021, "Red", false);
// Demonstrate polymorphism
Vehicle[] vehicles = { myCar, myTruck, myMotorcycle };
foreach (Vehicle vehicle in vehicles)
{
vehicle.DisplayInfo();
vehicle.StartEngine();
vehicle.Accelerate();
vehicle.Brake();
vehicle.StopEngine();
Console.WriteLine();
}
}
}
Vehicle Class: This is the base class that defines the common attributes and behaviors of all vehicles.
Make, Model, Year, Color: These are common attributes for all vehicles.IsEngineRunning: A boolean property to track the engine state.StartEngine(), StopEngine(), Accelerate(), Brake(): These are common behaviors for all vehicles. The virtual keyword allows derived classes to override these methods.DisplayInfo(): A method to display the vehicle's information.