Decorator Pattern in C#
Hello, fellow C# developers! In this post, I’m going to talk about one of my favorite design patterns: the Decorator Pattern. This pattern is very useful when you want to add some extra features or services to an existing object without changing its structure or creating a lot of subclasses. Sounds cool, right? š
What is the Decorator Pattern?
The Decorator Pattern is a structural design pattern that allows us to dynamically attach additional responsibilities or behaviors to an object at runtime. This is done by creating a decorator class that implements the same interface as the original object and wraps it inside. The decorator class can then delegate the original behavior to the wrapped object and add some new behavior on top of it.
The main benefit of this pattern is that it provides a flexible alternative to inheritance for extending functionality. Instead of creating a lot of subclasses that inherit from the original class and modify its behavior, we can simply use different decorators to compose the desired functionality at runtime. This way, we avoid the problems of subclassing, such as code duplication, tight coupling and increased complexity.
How to implement the Decorator Pattern in C#?
Let’s see how we can implement the Decorator Pattern in C# with a simple example. Suppose we have an interface called ILoan that defines the behavior of a loan:
// The 'Component' interface
public interface ILoan
{
// Returns the description of the loan
string GetDescription();
// Returns the interest rate of the loan
double GetInterestRate();
}
We can then create a concrete class that implements this interface, such as PersonalLoan:
// The 'ConcreteComponent' class
public class PersonalLoan : ILoan
{
public string GetDescription()
{
return "Personal loan";
}
public double GetInterestRate()
{
return 0.12; // 12% annual interest rate
}
}
Now, suppose we want to add some extra features or services to our loan, such as insurance, prepayment, or cashback. We could create subclasses for each combination of features, but that would be tedious and impractical. Instead, we can use decorators to add these features dynamically:
// The 'Decorator' abstract class
public abstract class LoanDecorator : ILoan
{
// The wrapped loan object
protected readonly ILoan loan;
// Constructor that takes an ILoan object as a parameter
public LoanDecorator(ILoan loanToDecorate)
{
this.loan = loanToDecorate;
}
// Delegates the GetDescription method to the wrapped loan object
public virtual string GetDescription()
{
return loan.GetDescription();
}
// Delegates the GetInterestRate method to the wrapped loan object
public virtual double GetInterestRate()
{
return loan.GetInterestRate();
}
}
// The 'ConcreteDecorator' class for adding insurance
public class InsuranceDecorator : LoanDecorator
{
// Constructor that calls the base constructor
public InsuranceDecorator(ILoan loan) : base(loan)
{
}
// Overrides the GetDescription method and adds insurance
public override string GetDescription()
{
return base.GetDescription() + " with insurance";
}
// Overrides the GetInterestRate method and adds 0.01 to the interest rate
public override double GetInterestRate()
{
return base.GetInterestRate() + 0.01; // 1% extra interest rate for insurance
}
}
// The 'ConcreteDecorator' class for adding prepayment option
public class PrepaymentDecorator : LoanDecorator
{
// Constructor that calls the base constructor
public PrepaymentDecorator(ILoan loan) : base(loan)
{
}
// Overrides the GetDescription method and adds prepayment option
public override string GetDescription()
{
return base.GetDescription() + " with prepayment option";
}
// Overrides the GetInterestRate method and subtracts 0.02 from the interest rate
public override double GetInterestRate()
{
return base.GetInterestRate() - 0.02; // 2% lower interest rate for prepayment option
}
}
// The 'ConcreteDecorator' class for adding cashback offer
public class CashbackDecorator : LoanDecorator
{
// Constructor that calls the base constructor
public CashbackDecorator(ILoan loan) : base(loan)
{
}
// Overrides the GetDescription method and adds cashback offer
public override string GetDescription()
{
return base.GetDescription() + " with cashback offer";
}
// Overrides the GetInterestRate method and adds 0.03 to the interest rate
public override double GetInterestRate()
{
return base.GetInterestRate() + 0.03; // 3% extra interest rate for cashback offer
}
}
With these decorators, we can now create any combination of features for our loan at runtime. For example, we can create a personal loan with insurance and prepayment option like this:
// Create a personal loan object
ILoan loan = new PersonalLoan();
// Wrap it with an insurance decorator
loan = new InsuranceDecorator(loan);
// Wrap it with a prepayment decorator
loan = new PrepaymentDecorator(loan);
// Print the description and the interest rate of the loan
Console.WriteLine(loan.GetDescription()); // Personal loan with insurance with prepayment option
Console.WriteLine(loan.GetInterestRate()); // 0.11 (11% annual interest rate)
Or we can create a personal loan with a cashback offer like this:
// Create a personal loan object
ILoan loan = new PersonalLoan();
// Wrap it with a cashback decorator
loan = new CashbackDecorator(loan);
// Print the description and the interest rate of the loan
Console.WriteLine(loan.GetDescription()); // Personal loan with cashback offer
Console.WriteLine(loan.GetInterestRate()); // 0.15 (15% annual interest rate)
As you can see, we can easily add or remove features from our loan without changing the original class or creating subclasses. This makes our code more flexible and maintainable. š
Where to learn more about the Decorator Pattern?
If you want to learn more about the Decorator Pattern in C#, I recommend you to check out these resources:
- Understand the "Decorator Pattern" with a real world example - Stack Overflow
- Decorator pattern - Wikipedia
- Decorator pattern: explanation, UML presentation, and example - IONOS
- Decorator Pattern - Javatpoint
I hope you enjoyed this post and learned something new about the Decorator Pattern in C#. This pattern is very powerful and can help you create elegant and extensible code in C#. Feel free to share your thoughts or questions in the comments below.
You can find the code examples here: source code
Happy coding! š
Comments
Post a Comment