Introduction
We have discussed in our previous article the decorator pattern’s role, and given a very detailed illustration about it. Also, we have discussed its design varieties and clarified when to use each design and why.
Today, we will complete our discussion about decorator pattern. We will talk deeply about different examples from the real world cases to give you a deeper understanding of this pattern. Also, we will discuss when to use this pattern in real world situations.
We will demonstrate first a simple example and then move on to more complicated ones and more variations.
1. Example: Use Case Diagram Tool
Use case diagram is one of the fundamental diagrams of the unified modeling language (UML). We use the use case diagrams to describe all the use cases a user can interact with within an application.
Here, we will start to build a simple (Really, it is a very simple, just for demonstrations purposes) use case diagram tool.
If we look more thoughtfully into the available shapes that can be drawn in a use case diagram, we will find them to be 3 main shapes:
- Actors
- Use cases
- Arrows (which represent relationships between actors, or between use cases or between actors and use cases)
But a deeper look, we will find that each shape has a different look in different situations.
As described in the following diagram, some of the variations that could be applied to any shape during drawing process of use case diagrams:
The challenge here is that we want to build an application that covers all the variations available and with an ability to add more shapes in future in case any updates happen to UML.
Here comes the magic of the decorator pattern. Let’s get started:
Analyzing the last diagram shows us that we have 3 main shapes with different variations. Variations means here decorators which can be applied to different shapes to show us the right final shape.
More analyzing, we are noticing that we have some kind of hybrid decorators like for example, dotted line with a tag. In fact, they are two decorators tag decorator and dotted line decorator.
Let’s first start with one shape and then we will elaborate on this.
IShape Interface and Actor Class
IShape Interface
public interface IShape
{
Bitmap Draw();
}
Actor Class
public class Actor : IShape
{
public Bitmap Draw()
{
Bitmap image = new Bitmap(200, 200);
using (Graphics graphics = Graphics.FromImage(image))
{
using (Pen blackPen = new Pen(Brushes.Black))
{
graphics.DrawEllipse(blackPen, 100, 20, 20, 20);
graphics.DrawLine(blackPen, 110, 40, 110, 80);
graphics.DrawLine(blackPen, 110, 55, 130, 55);
graphics.DrawLine(blackPen, 110, 55, 90, 55);
graphics.DrawLine(blackPen, 110, 80, 130, 100);
graphics.DrawLine(blackPen, 110, 80, 90, 100);
}
}
return image;
}
}
If we try now to use this class with a Windows Forms application in .NET, for example, you will write just like the following code after dragging a picture box into the form:
Actor actor = new Actor();
pictureBox1.Image = actor.Draw();
Run the project, you will see:
Now we need to add some sugar, so let’s add a decorator called TagDecorator
which will be responsible for adding tags (titles) to any shape.
Here is the class diagram (following the guidelines of decorator patterns):
TagDecorator Class
public class TagDecorator : IShape
{
private IShape _shape;
private string _tag;
public TagDecorator(IShape shape, string tag)
{
_shape = shape;
_tag = tag;
}
public Bitmap Draw()
{
Bitmap image = _shape.Draw();
using (Graphics grahpics = Graphics.FromImage(image))
{
grahpics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
StringFormat stringFormat = new StringFormat
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
};
grahpics.DrawString(_tag, new Font("Times New Roman", 12),
Brushes.Black, new RectangleF(10, 110, 200, 20), stringFormat);
}
return image;
}
}
Let’s try to draw this decorated actor to the form. So we will write this code:
Actor actor = new Actor();
TagDecorator actorTag = new TagDecorator(actor, "User");
pictureBox1.Image = actorTag.Draw();
And see the output:
Now let’s make things more complicated, I will just give you the concepts and let you try to implement them.
What if we want to add more shapes, simply we will use the following diagram:
What about more decorators?!
Go, and try to build your own diagramming tool.
2. Real World Examples
Now we will demonstrate some real world examples may be we are using in a daily basis but we don’t know.
In a very good article by Rob Pierry, he discussed when a decorator pattern has been used within .NET. He said:
“Any useful executable program involves either reading input, writing output, or both. Regardless of the source of the data being read or written, it can be treated abstractly as a sequence of bytes. .NET uses the System.IO.Stream
class to represent this abstraction. Whether the data involves characters in a text file, TCP/IP network traffic, or something else entirely, chances are you will have access to it via a Stream
. Since the class for working with file data (FileStream
) and the class for working with network traffic (NetworkStream
) both inherit from Stream
, you can easily write code that processes the data independent of its origins. Here’s a method for printing out some bytes from a Stream
to the console:
public static void PrintBytes(Stream s)
{
int b;
while((b = fs.ReadByte()) >= 0)
{
Console.Write(b + " ");
}
}
Reading a single byte at a time is typically not the most efficient way to access a stream
. For example, hard drives are capable of (and optimized for) reading continuous blocks of data from the disk in a big chunk. If you know you are going to be reading several characters, it is better to read a chunk from the disk all at once and then consume the chunk from memory byte by byte. The Framework includes the BufferedStream
class for doing just that. The constructor for BufferedStream
takes as the parameter whatever stream
you would like buffered access to. BufferedStream
overrides the main methods of Stream
, such as Read
and Write
, to provide more functionality. Since it is still a child class of Stream
, you can use it the same as any other Stream
(note that FileStream
includes its own buffering capabilities). Similarly, you can use System.Security.Cryptography.CryptoStream
to encrypt and decrypt Streams on the fly, without the rest of the application needing to know anything more than the fact that it is a Stream
.
This ability to dynamically attach new functionality to objects transparently using composition is an example of the Decorator pattern”.
This is valid also for Java developers as well as far as I know in java.io
namespace.
Let’s also demonstrate more general real examples:
Here are four ways the Decorator pattern is used in the real world (Excerpt from C# 3.0 Design patterns):
- As our small example illustrated, the Decorator pattern fits well in the graphics world. It is equally at home with video and sound; for instance, video streaming can be compressed at different rates, and sound can be input to a simultaneous translation service.
- At a more mundane level, decorators abound in the I/O APIs of C# as illustrated.
- In today’s world of mobile devices, web browsers and other mobile applications thrive on the Decorator pattern. They can create display objects suitable for smaller screens that include scroll bars and exclude banners that would be standard on desktop display browsers, for example.
- The Decorator pattern is so useful that there are now actual Decorator classes in .NET 3.0. The one in
System.Windows.Controls
“provides a base class for elements that apply effects onto or around a single child element, such as Border
or Viewbox
.”
3. When to Use?
When you have:
- An existing component class that may be unavailable for subclassing
Or when you want to:
- Attach additional state or behavior to an object dynamically
- Make changes to some objects in a class without affecting others
- Avoid subclassing because too many classes could result
4. Finally
At last, we have finished demonstrating this pattern and its details. Your feedback is welcome.