The Decorator pattern allows to attach additional responsibilities to an object dynamically. It is an alternative to subclassing for extending behaviour.
Instead of creating a silly example of usage I decided to rely on one of the best example of implementation that is already available in the .NET Framework: the Streams.
The following class diagram is a restricted view of the Streams hierarchy in .NET:
Recognising the pattern is usually quite simple. When you see an object that inherit from a base class (Stream) and at the same time has a constructor that accept an element of the same base class (Stream), you probably came across to a Decorator.
The idea is that you can create an object that wraps an another object (of the same type) in order to provide new functionalities and the resulting object can be wrapped again creating a chain of objects that together provide a combination of functionalities. The beauty is that any single object is unaware that it is wrapped and this allows to create any combinations of them at runtime. If you consider the alternative of creating a single class for each combination of functionalities, the flexibility you get with the Decorator pattern is immense.
In the previous diagram there are three Decorators:
- BufferedStream
- CryptoStream
- GZipStream
But what about the others classes?
FileStream, MemoryStream, NetworkStream does not have a constructor that takes a Stream. They simply are Components and not Decorators. This means that these objects can only be wrapped and cannot wrap other objects. These objects represent the end of the chain.
Yes, it seems quite complicated but it is not. Let's make a concrete example.
Problem
Create a TCP client that allows you to send a message over the network to a TCP server. Before sending, the message will be buffered, compressed and then ciphered. The server will do the opposite operations in order to receive the message correctly.
Streams represents the best way to implement this scenario.
Solution
Solution
This is the code that creates a TCP client and send the message over the network:
From the example, it should be easy to figure out how the wrapping process works. Wrapping is the way to add functionality in a dynamic way. If you don't want to compress the data, you can simply remove the gzipStream wrapper. In addition, you can also change the order in which you want to execute the operations, simply wrapping in a different order. The decorator pattern provides you this flexibility.
The server side code is symmetric except that I decided to write the output to a file instead, using a FileStream.
Just for completeness this is the entry point of the program:
When you run the program the client send the message, the server receives and saves the message in a file that will be opened at the end.
The main Object Oriented Design principle respected by the decorator pattern is the Open/Close Principle.
Software entities should be open for extension, but closed for modification.
What? How can you extend the system without changing it?
You can create a completely new class that inherit from Stream and add it into the chain in any position you want.
The streams hierarchy provided by .NET is open for extension but you don't need to change the code of the framework to do this (it is closed for modification).
Decorator Pattern - Streams in .NET
Great post my friend .. greet finishing by mentioning ocp.
ReplyDeleteMozafar
Thank you my friend!
ReplyDeletegood
ReplyDeleteThanks
Delete