Decorator Pattern: Closed For Modification But Open For Extension
Decorator Pattern: Closed For Modification But Open For Extension
Decorator Pattern
One of the most important aspects of the development process that developers and programmers have to grapple with is change, which is why design patterns were introduced in the first place. In particular, design patterns are intended to help you handle change as you have to adapt your code to new and unforeseen circumstances. Developers spend much more time extending and changing code than they do originally developing it
Design Pattern
Role
The role of the Decorator pattern is to provide a way of attaching new state and behavior to an object dynamically. The object does not know it is being decorated, which makes this a useful pattern for evolving systems. A key implementation point in the Decorator pattern is that decorators both inherit the original class and contain an instantiation of it.
Illustration
As its name suggests, the Decorator pattern takes an existing object and adds to it. As an example, consider a photo that is displayed on a screen. There are many ways to add to the photo, such as putting a border around it or specifying tags related to thecontent. Such additions can be displayed on top of the photo, as shown in Figure.
6/12/2012
The combination of the original photo and some new content forms a new object. In the second image shown , there are four objects:
the original photo as shown to the left, the object that provides a border, and two tag objects with different data associated with them. Each of them is a Decorator object. Given that the number of ways of decorating photos is endless, we can have many such new objects.
Design
Now, we can specify the players in the Decorator pattern in a UML diagram. The essential players in this UML diagram are: Component - An original class of objects that can have operations added or modified (there may be more than one such class) Operation - An operation in IComponent objects that can be replaced (there may be several operations) Icomponent - The interface that identifies the classes of objects that can be decorated (Component is one of these) Decorator - A class that conforms to the IComponent interface and adds state and/or behavior (there may be more than one such class)
6/12/2012
Has-a - The has-a relationship is shown by an open diamond on the Decorator, linked to IComponent. This indicates that the Decorator instantiates one or more IComponent objects and that decorated objects can outlive the originals. The Decorator uses the component attribute (of type IComponent) to invoke any replacement Operation it might wish to override. This is the way the Decorator pattern achieves its objective.
The addedBehavior operation and the addedState attribute in the Decorator class are other optional ways of extending what is in the original Component objects.
From this list, we can see that the following would be valid statements in a Client wanting to put two tags on a photo: Photo photo = new Photo( ); Tag foodTag = new Tag (photo, "Food",1); Tag colorTag = new Tag (foodTag, "Yellow",2);
By the is-a relationship, photo, foodTag, and colorTag are all IComponent objects. Each of the tags (the decorators) is created with a Component, which might be a photo or an already tagged photo.
Multiple Guises
Multiple components Different components that conform to the interface can also be decorated. For example, we could have a class that draws people, houses, ships, and so on from simple shapes and lines. They too could be tagged. It is for this reason that the IComponent interface is important, even if it does not contain any operations. In the case where we are sure there will only ever be one class of components, we can dispense with the IComponent interface and have the decorators directly inherit from Component.
Multiple Guises
Multiple decorators We have seen that we can create different instances of a Tag decorator. We can also consider having other types of decorators, such as Border decorators or even decorators that make the photo invisible. No matter what the decorators are, each contains a component object, which might itself be a decorator, setting off a chain of changes
6/12/2012
Multiple Guises
Multiple operations Our illustration focuses on drawing as the chief operation for photos and decorations. Other examples will lend themselves to many more optional operations. Some of these will be part of the original component and its interface, whereas some will be added behaviors in certain decorators only. The client can call any of the operations individually on any of the components (decorated or otherwise) to which it has access.
Implementation
The Decorator patterns key feature: It does not rely on inheritance for extending behavior. If the Tag class had to inherit from the Photo class to add one or two methods, Tags would carry everything concerned with Photos around with them,making them very heavyweight objects. Instead, having the Tag class implement a Photo interface and then add behavior keeps the Tag objects lean. They can:
Implement any methods in the interface, changing the initial behavior of the component Add any new state and behavior Access any public members via the object passed at construction
class DecoratorA : IComponent { IComponent component; public DecoratorA (IComponent c) { component = c; } public string Operation( ) { string s = component.Operation( ); s += "and listening to Classic FM "; return s; } }
class DecoratorB : IComponent { IComponent component; public string addedState = "past the Coffee Shop "; public DecoratorB (IComponent c) { component = c; } public string Operation ( ) { string s = component.Operation ( ); s += "to school "; return s; } public string AddedBehavior( ) { return "and I bought a cappuccino "; } }
class Client { static void Display(string s, IComponent c) { Console.WriteLine(s+ c.Operation( )); } } static void Main( ) { Console.WriteLine("Decorator Pattern\n"); IComponent component = new Component( ); Console.Writeline("1. Basic component: ", component); Console.Writeline("2. A-decorated : ", new DecoratorA(component)); Console.Writeline("3. B-decorated : ", new DecoratorB(component)); Console.Writeline("4. B-A-decorated : ", new DecoratorB(new DecoratorA(component))); // Explicit DecoratorB DecoratorB b = new DecoratorB(new Component( )); Display("5. A-B-decorated : ", new DecoratorA(b)); // Invoking its added state and added behavior Console.WriteLine("\t\t\t"+b.addedState + b.AddedBehavior( )); }
6/12/2012
Use
/* Output Decorator Pattern 1. Basic component: I am walking 2. A-decorated : I am walking and listening to Classic FM 3. B-decorated : I am walking to school 4. B-A-decorated : I am walking and listening to Classic FM to school 5. A-B-decorated : I am walking to school and listening to Classic FM past the Coffee Shop and I bought a cappuccino
Here are four ways the Decorator pattern is used in the real world:
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.
Use
At a more mundane level, decorators abound in the I/O APIs of C#. Consider the following hierarchy:
System.IO.Stream System.IO.BufferedStream System.IO.FileStream System.IO.MemoryStream System.Net.Sockets.NetworkStream System.Security.Cryptography.CryptoStream
Use
In todays 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 subclasses decorate Stream because they inherit from it, and they also contain an instance of a Stream that is set up when an object is constructed. Many of their properties and methods relate to this instance
Use
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.
6/12/2012