Ever wanted to trick out your ride with all the cool extras—like a sports package or a luxury upgrade—without buying a whole new car? That’s what the Decorator Pattern does for your code. It lets you dynamically add new features to an object without altering its core, just like customizing a basic car to suit your style.
Why Bother with It?
- Custom Upgrades on Demand: Just as you can add a fancy spoiler or premium sound system to a car, the Decorator Pattern lets you “decorate” an object with additional responsibilities as needed.
- Flexible Enhancements: Instead of creating a ton of subclasses for every possible combination of features, you can mix and match decorators to get exactly what you want.
- Keep the Core Intact: Your base car remains the same; you’re simply adding layers of functionality—without rewriting the blueprint every time.
How It Works (In Plain English)
- The Base Car: Start with a simple car (your core object) that has basic features.
- The Decorators: Create add-ons (decorators) that wrap around the base car to add new capabilities—like a sports package or a luxury package.
- Mix and Match: Wrap your car with one or more decorators to get a fully customized ride, all while keeping the original structure intact.
C# Code Example
C#
using System;
// The component interface representing a car.
public interface ICar
{
string GetDescription();
double GetCost();
}
// Concrete component: A basic car.
public class BasicCar : ICar
{
public string GetDescription() => "Basic Car";
public double GetCost() => 15000;
}
// Abstract decorator class that implements ICar.
public abstract class CarDecorator : ICar
{
protected ICar _car;
public CarDecorator(ICar car)
{
_car = car;
}
public virtual string GetDescription() => _car.GetDescription();
public virtual double GetCost() => _car.GetCost();
}
// Concrete decorator: Adds a sports package.
public class SportsPackage : CarDecorator
{
public SportsPackage(ICar car) : base(car) { }
public override string GetDescription() => _car.GetDescription() + ", Sports Package";
public override double GetCost() => _car.GetCost() + 5000;
}
// Concrete decorator: Adds a luxury package.
public class LuxuryPackage : CarDecorator
{
public LuxuryPackage(ICar car) : base(car) { }
public override string GetDescription() => _car.GetDescription() + ", Luxury Package";
public override double GetCost() => _car.GetCost() + 8000;
}
// Client code: customizing your car.
public class Program
{
public static void Main()
{
ICar car = new BasicCar();
Console.WriteLine(car.GetDescription() + " costs $" + car.GetCost());
// Add a sports package.
car = new SportsPackage(car);
Console.WriteLine(car.GetDescription() + " costs $" + car.GetCost());
// Add a luxury package on top.
car = new LuxuryPackage(car);
Console.WriteLine(car.GetDescription() + " costs $" + car.GetCost());
}
}
The Takeaway
The Decorator Pattern is like having a top-notch car customization shop. You start with a basic model and then add exactly the features you want—be it a sporty edge or a touch of luxury—without altering the original blueprint. This approach makes your code more flexible and avoids the need for a separate subclass for every combination of features.
Happy customizing, and drive your code in style!