In this article, let's learn about
Decorator Design Pattern in .NET.
Table of Contents
decorator pattern is a design pattern that allow
dynamically attach additional
responsibilities to an object, providing a
flexible alternative to subclassing for
extending functionality. Also referred to as a
wrapper, the decorator pattern allows for the
dynamic addition of responsibilities to an object at runtime.
By using the decorator pattern, we can
avoid violating Single Responsibility Principle. Our classes would get
littered with code that doesn't necessarily belong there. That's where the decorator pattern comes in.
- Defines the
interfacefor objects that can have responsibilities added to them.
Concrete Component (DatabaseRepository):
- Defines the
logicto read data from database.
abstract classimplementing the same
componentand maintains a reference to a
component object. It defines an
interfacethat conforms to the component's
Concrete Decorator (CachedRepositoryDecorator):
- Implements the decorator
abstract classand adds specific behavior or state to the component.
Code Sample - Code Sample - Decorator Pattern
In the above code, we're implementing the
decorator pattern for a
IRepository interface defines the base functionality with a
DatabaseRepository class is a
concrete component that retrieves data
from a database. The
RepositoryDecoratorBase abstract class acts as the
maintaining a reference to a repository and
ReadData method. Finally,
CachedDatabaseRepository class is a
concrete decorator that extends the behavior
by adding caching logic. If a random probability is less than 20%, it returns cached data; otherwise, it delegates to the base repository's
ReadData method. This
composition of repository behaviors, adding features like caching without modifying the core repository code.
Demo - Decorator Pattern Demo
Decorator Demo, Click on the
Read via Decorator Button to
see the demo on the screen. Click the button multiple times to see the data being read from cache or from database based on cache availability.
For demo purpose, radomness is introduced to simulate the cache availability. In reality, it could be based on time.
Code Sample - Decorator Pattern Demo
Reading Data Using Decorator :
Dynamic Responsibility Addition- When there's a need to add responsibilities to individual objects dynamically at runtime without affecting other objects.
Dynamic Responsibility Removal- When the added responsibilities need to be withdrawn dynamically.
Extension by Subclass is Impractical- When extending functionality through subclassing results in an impractical or impossible solution.
Flexibility- Dynamically add or remove responsibilities at runtime.
Single Responsibility Principle- Helps adhere to the single responsibility principle by separating concerns.
System Litter - This pattern may result in a system with many small, simple classes, potentially increasing the effort required for learning and debugging. Despite
these points, the Decorator pattern is a useful starting point because of its simplicity. Finally, let's have a look at related patterns.
In this article we learn't about the
decorator pattern that provides a
extend the functionality of objects dynamically at runtime. It promotes flexibility and adherence to the
single responsibility principle, although it may lead to a system with
many small classes.
Understanding its structure and use cases enables developers to leverage its benefits effectively.