Structural Design Pattern - Decorator
Design Pattern
10 Articles
In this article, let's learn about Decorator Design Pattern
in .NET.
Table of Contents
Introduction
The 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.
Structure
Component (IRepository):
- Defines the
interface
for objects that can have responsibilities added to them. Concrete Component (DatabaseRepository):
- Defines the
logic
to read data from database. Decorator (RepositoryDecoratorBase):
-
An
abstract class
implementing the sameinterface
as thecomponent
and maintains a reference to acomponent object
. It defines aninterface
that conforms to the component'sinterface
. Concrete Decorator (CachedRepositoryDecorator):
- Implements the decorator
abstract class
and 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 repository
system. The IRepository interface
defines the base functionality with a ReadData
method. The DatabaseRepository class
is a concrete component
that retrieves data
from a database. The RepositoryDecoratorBase abstract class
acts as the decorator base
,
maintaining a reference to a repository and delegating
the ReadData
method. Finally,
the 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 structure
allows dynamic
composition of repository behaviors, adding features like caching without modifying the core repository code.
Demo - Decorator Pattern Demo
Let's try 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 :
Use Cases
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.
Advantages
Flexibility
- Dynamically add or remove responsibilities at runtime.Single Responsibility Principle
- Helps adhere to the single responsibility principle by separating concerns.
Disadvantages
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.
Related Patterns
adapter
patterncomposite
patternstrategy
pattern
Summary
In this article we learn't about the decorator
pattern that provides a flexible
way to 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.