Decorator pattern allows to add new functionality an existing object without altering its structure. This type of design pattern comes under structural pattern as this pattern acts as a wrapper to existing class.
This pattern creates a decorator class which wraps the original class and provides additional functionality keeping class methods signature intact.
As per GOF Design pattern , definition of Decorator Design pattern is “Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.”
The decorator pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub classing for extending functionality.
When
to use ?
- A data whenever changed should be distributed to all the relevant subscribers immediately.
- Whenever a subscriber unsubscribes for the data the code pertaining to the publisher should not change.
- Whenever new subscriber subscribes for the data the code pertaining to the publisher should not change.
The decorator pattern is a structural design pattern. Whereas inheritance adds functionality to classes, the decorator pattern adds functionality to objects by wrapping objects in other objects. Each time additional functionality is required, the object is wrapped in another object. Java I/O streams are a well-known example of the decorator pattern.
To
extend or modify the behavior of ‘an instance’ at runtime decorator design pattern is used. Inheritance is
used to extend the abilities of ‘a class’. Unlike inheritance, you can choose
any single object of a class and modify its behaviour leaving the other
instances unmodified.
The Decorator design pattern takes a
different approach. Instead of using external
algorithms, this design pattern is all about using
wrapper code to extend your core code.
Decorator design pattern
is used to modify the functionality of an object at runtime. At the same time
other instances of the same class will not be affected by this, so individual
object gets the modified behavior. Decorator design pattern is one of the
structural design pattern and uses abstract classes or interface with composition
to implement.
We use inheritance
or composition to extend the behavior of an object but this is done at compile
time and it’s applicable to all the instances of the class. We can’t add any
new functionality of remove any existing behavior at runtime – this is when
Decorator pattern comes into picture.
Example
1 : While purchasing computer we have
multiple things bounded to values
We can start coding by creating the
core component, which is the computer the example the computer itself. Now how about creating some decorator
classes? Such classes have to act as wrappers for the Computer class, which
means that variables that can hold computer
objects should also be able to hold
objects that wrap computer objects. And one easy way to make sure that can
happen is to extend the wrapper classes from the Computer class.
1: public class Computer {
2: public Computer() { }
3: public String description(){
4: return "computer";
5: }
6: }
Alright, that’s the core component:
the computer itself. Such classes have to act as wrappers for the Computer
class, which means that variables that can hold computer objects should also be able to hold
objects that wrap computer objects. And one easy way to make sure that
can happen is to extend the wrapper classes from the Computer class.
Creating
a decorator
You
might start by creating an abstract class that all Computer
class wrappers have to extend
1: public abstract class ComponentDecorator extends Computer {
2: public abstract String description();
3: }
1: public class Disk extends ComponentDecorator {
2: Computer c ;
3: public Disk(Computer a) {
4: c = a;
5: }
6: @Override
7: public String description() {
8: return c.description() + " and a Disk";
9: }
10: }
1: public class Monitor extends ComponentDecorator {
2: Computer computer;
3: public Monitor(Computer c){
4: computer = c;
5: }
6: @Override
7: public String description() {
8: return computer.description() + " and Monitor";
9: }
10: }
1: public class CD extends ComponentDecorator {
2: Computer computer;
3: public CD(Computer c){
4: computer = c;
5: }
6: @Override
7: public String description() {
8: return computer.description() + " and with CD ";
9: }
10: }
1: public class DecoTest {
2: public static void main(String[] args) {
3: Computer computer = new Computer();
4: computer = new Disk(computer);
5: computer = new Monitor(computer);
6: computer = new CD(computer);
7: computer = new CD(computer);
8: System.out.println("You’re getting a " + computer.description() + ".");
9: }
10: }
Output
will be like below
You’re getting a computer and a
Disk and Monitor and with CD and with CD.
Example 2 : Following given
example is an implementation of decorator design pattern. Icecream is a classic
example for decorator design pattern. You create a basic icecream and then add
toppings to it as you prefer. The added toppings change the taste of the basic
icecream. You can add as many topping as you want.
The above is an
interface depicting an icecream
1: public interface IceCream {
2: public String makeIcecream();
3: }
1: public class SimpleIcecream implements IceCream {
2: @Override
3: public String makeIcecream() {
4: return "Base Ice Cream";
5: }
6: }
It is the core of the decorator
design pattern. It contains an attribute for the type of interface. Instance is
assigned dynamically at the creation of decorator using its constructor
Following
class is the decorator class. It is the core of the decorator design pattern.
It contains an attribute for the type of interface. Instance is assigned
dynamically at the creation of decorator using its constructor. Once assigned
that instance method will be invoked.
1: public abstract class IcecreamDecorator implements IceCream {
2: protected IceCream iceCream ;
3: public IcecreamDecorator(IceCream i) {
4: iceCream = i;
5: }
6: @Override
7: public String makeIcecream() {
8: return iceCream.makeIcecream();
9: }
10: }
Following two classes are similar.
These are two decorators, concrete class implementing the abstract decorator.
When the decorator is created the base instance is passed using the constructor
and is assigned to the super class. In the makeIcecream method we call the base
method followed by its own method addNuts() and getHoeny()
1: public class NuttyDecorator extends IcecreamDecorator {
2: public NuttyDecorator(IceCream i) {
3: super(i);
4: }
5: public String makeIcecream() {
6: return iceCream.makeIcecream() + addNuts();
7: }
8: private String addNuts() {
9: return " + Nuts ";
10: }
11: }
1: public class HoneyDecorator extends IcecreamDecorator {
2: public HoneyDecorator(IceCream i) {
3: super(i);
4: }
5: @Override
6: public String makeIcecream() {
7: return iceCream.makeIcecream() + getHoney();
8: }
9: private String getHoney() {
10: return " + Real Honey";
11: }
12: }
I have created a simple icecream and
decorated that with nuts and on top of it with honey. We can use as many
decorators in any order we want. This excellent flexibility and changing the
behaviour of an instance of our choice at runtime is the main advantage of the
decorator design pattern.
1: public class TestDecorator {
2: public static void main(String[] args) {
3: IceCream icecream = new HoneyDecorator(new NuttyDecorator(new SimpleIcecream()));
4: System.out.println(icecream.makeIcecream());
5: }
6: }
Output will be like below
Base Ice Cream + Nuts + Real Honey
Decorator Design Pattern in java API
java.io.BufferedReader;java.io.FileReader;
java.io.Reader
A typical usage of Decorator pattern
is Java IO classes.
Here is a simple example –
BufferedReader decorates InputStreamReader.
BufferedReader
input = new
BufferedReader(new InputStreamReader(System.in));
//System.in is an InputStream object
|
InputStreamReader(InputStream
in)
– bridge from byte streams to character streams. InputSteamReader reads bytes
and translates them into characters using the specified character encoding.
BufferedReader(Reader
in)
– read text from a character stream and buffer characters in order to provide
efficient reading methods(e.g., readLine())
Consequences
- Decoration is more convenient for adding functionalities to objects instead of entire classes at runtime. With decoration it is also possible to remove the added functionalities dynamically.
- Decoration adds functionality to objects at runtime which would make debugging system functionality harder.
Related
Patterns
- Adapter Pattern - A decorator is different from an adapter in that a decorator changes object's responsibilities, while an adapter changes an object interface.
- Composite Pattern - A decorator can be viewed as a degenerate composite with only one component. However, a decorator adds additional responsibilities.
Notes
:
Advantage
: No explosion of classes. Maintenance is easy.
Can you explain the decorator design pattern?
A. By implementing the decorator pattern you construct a wrapper around an object by extending its behavior. The wrapper will do its job before or after and delegate the call to the wrapped instance. The decoration happens at run-time. In Java, the wrapper classes like Integer, Double, etc are typical example of a decorator pattern. Another good example is the Java I/O classes as shown below. Each reader or writer will decorate the other to extend or modify the behavior.
A. By implementing the decorator pattern you construct a wrapper around an object by extending its behavior. The wrapper will do its job before or after and delegate the call to the wrapped instance. The decoration happens at run-time. In Java, the wrapper classes like Integer, Double, etc are typical example of a decorator pattern. Another good example is the Java I/O classes as shown below. Each reader or writer will decorate the other to extend or modify the behavior.
1
2
3
4
5
|
String inputText =
"Some text to read";
ByteArrayInputStream
bais = new ByteArrayInputStream(inputText.getBytes());
Reader isr = new InputStreamReader(bais);
BufferedReader br =
new BufferedReader(isr);
br.readLine();
|
As you can see, each reader extends the behavior at run-time. This is the power
of object composition as opposed to inheritance. By composing a fewer
classes at run-time, desired behavior can be created. Here is another example
demonstrating an interleaved reading using a class from the Apache library.
Q. How does a decorator design pattern
differ from a proxy design pattern?
A. In Proxy pattern, you have a proxy and a real subject. The relationship between a proxy and the real subject is typically set at compile time, whereas decorators can be recursively constructed at run time. The Decorator Pattern is also known as the Wrapper pattern. The Proxy Pattern is also known as the Surrogate pattern. The purpose of decorator pattern is to add additional responsibilities to an object. These responsibilities can of course be added through inheritance, but composition provides better flexibility as explained above via the Java I/O classes. The purpose of the proxy pattern is to add an intermediate between the client and the target object. This intermediate shares the same interface as the target object. Here are some scenarios in which a proxy pattern can be applied.
A. In Proxy pattern, you have a proxy and a real subject. The relationship between a proxy and the real subject is typically set at compile time, whereas decorators can be recursively constructed at run time. The Decorator Pattern is also known as the Wrapper pattern. The Proxy Pattern is also known as the Surrogate pattern. The purpose of decorator pattern is to add additional responsibilities to an object. These responsibilities can of course be added through inheritance, but composition provides better flexibility as explained above via the Java I/O classes. The purpose of the proxy pattern is to add an intermediate between the client and the target object. This intermediate shares the same interface as the target object. Here are some scenarios in which a proxy pattern can be applied.
- A remote proxy provides a local representative for an object in a different address space.Providing interface for remote resources such as web service or REST resources or EJB using RMI.
- A virtual proxy creates expensive object on demand.
- A protection proxy controls access to the original object.Protection proxies are useful when objects should have different access rights.
- A smart reference is a replacement for a bare pointer that performs additional actions when an object is accessed.
- Adding a thread-safe feature to an existing class without changing the existing class's code. This is useful when you do not have the freedom to fix thread-safety issues in a third-party library.
Difference between Decorator and Proxy pattern in Java?
Another
tricky Java design pattern question and trick here is that both Decorator and
Proxy implements interface of the object they decorate or encapsulate. As I
said, many Java design pattern can have similar or exactly same structure but
they differ in there intent. Decorator pattern is used to implement
functionality on already created object, while Proxy pattern is used for
controlling access to object. One more difference between Decorator and Proxy
design pattern is that, Decorator doesn't create object, instead it get object
in it's constructor, while Proxy actually creates objects.
No comments:
Post a Comment