In C#, using the Decorator design pattern involves creating a set of decorator classes that are used to wrap concrete components. Here is a concrete example and some common use cases.
Let’s create a simple example involving a Notifier
system where we can send notifications via different channels (Email, SMS, etc.).
Step 1: Create a Component Interface
This step is essential to define the contract that the decorators will need to implement.
|
|
Step 2: Create a Concrete Component
|
|
Step 3: Create Base Decorator Class
|
|
Step 4: Create Concrete Decorators
|
|
Step 5: Use Decorators
Let’s put the code to the test:
|
|
What’s the Output?
|
|
Step by Step Explanation
The first test is simple. Nothing special to explain.
The second, third and fourth tests show the Decorator pattern in action.
If you look at the third test, facebookNotifier
wraps smsNotifier
, itself wrapping emailNotifier
.
emailNotifier
is the base and when you call facebookNotifier.Send("Hello World!")
, here’s what happens:
-
FacebookNotifier.Send
is called and the first thing happening within the method is a call tobase.Send(message)
(which issmsNotifier.Send
). -
So then
SMSNotifier.Send
is called. Again the first thing happening within the method is a call tobase.Send(message)
(which isemailNotifier.Send
). -
So
EmailNotifier.Send
is called and it prints “Sending Email: Hello World!”. -
Then control returns to
SMSNotifier.Send
, which prints “And sending SMS: Hello World!”. -
And finally control returns to
FacebookNotifier.Send
, which prints “And posting on Facebook: Hello World!”.
Each decorator adds its own behavior after calling the wrapped object’s method.
|
|
Common Use Cases
When you want to:
- Extend Functionality: When you want to add responsibilities to individual objects, not to an entire class.
- Combine Behaviors: When you need to add a combination of behaviors at runtime. For example, combining various logging, authentication, or notification behaviors.
- Adhere to Single Responsibility Principle: By using decorators, you can divide a class’s functionality into separate classes with specific responsibilities.
- Build User Interface: Wrapping user interface elements to add responsibilities like borders, scrollbars, or decorations.
- Input/Output Streams: Adding responsibilities to streams (e.g., adding buffering, encryption, compression, etc.).
Sources
The resources below provide a comprehensive understanding of the Decorator pattern and its applications in software design.
- Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (Gang of Four)
- Head First Design Patterns by Eric Freeman, Elisabeth Robson
- Microsoft Documentation on the Decorator Pattern
Follow me
Thanks for reading this article. Make sure to follow me on X, subscribe to my Substack publication and bookmark my blog to read more in the future.
Photo by Nataliya Vaitkevich