Decorator Design Pattern Example

Decorator Design Pattern: The Decorator design pattern adds additional responsibilities to an object dynamically without affecting other objects or existing objects. It is like a wrapper on another object.

In other words, The decorator pattern is used to extend (decorate) the functionality of a certain object statically, or dynamically.
The decorator pattern is an alternative to subclassing. We can write a subclass by extending a class and add behavior at compile time, and the change affects all object instances of the original class. 
But we can achieve this same behavior without extending a class and using a decorator class, since decorating can provide new behavior at run-time for individual objects.

Lets take an example to understand it.
Problem: We need to read a text file and display its content on screen. So we can write a InputReader class which will take a file as input and read the text file and display the content.

Now suppose:
  1. The given file has text with lots of extra white spaces, So using this InputReader how we can read the file and display the text by removing extra white spaces.  i.e "abc       xyz" shoud be displayed as "abc xyz".
  2. The given text file is an encrypted file and before displaying the text on screen it has to be decrypt. So using the InputReader class how we can achieve this?
The UML diagram of InputReader class can be show below
Interface Reader.java can be written as

public interface Reader {
          String read();
}

Class InputReader.java can be written as

public class InputReader implements Reader {  
        private String  file;

public InputReader(String file) {
this.file = file;
}

@Override
public String read() {
if (file!=null) {
// read file here and return data
}
return "this0is01st0line         \n         this0is02nd0line";//this is just a sample text
}
}

The traditional way of achieving the above functionalities is, by extending the InputReader class and override the read() method in each class. But without extending the InputReader class how we can achieve this?

Solution: We can achieve both the functionalities by using decorator design pattern. So we will introduce a abstract decorator class say AbstractFileReader and will provide the concrete implementation of  AbstractFileReader in SimpleFileReader and EncryptFileReader classes.

The class diagram using decorator be shown as following

Lets write the code for the shown classes.
Here we will write the AbstractFileReader.java, this class is a decorator for InputReader class.

abstract public class AbstractFileReader implements Reader {
protected Reader reader;

public AbstractFileReader(Reader reader) {
this.reader = reader;
}

abstract public String getDescription();
}

The SimpleFileReader this will remove the extra white spaces from the text data after reading the text file.
SimpleFileReader.java class is a concrete implementation of  AbstractFileReader.

public class SimpleFileReader extends AbstractFileReader{

public SimpleFileReader(Reader reader){
super(reader);
}
 
@Override
public String read(){
//this will remove extra space from the file data
return reader.read().replace(" ", "");
}

@Override
public String getDescription() {
return "Simple file Reader";
}
}

The EncryptFileReader.java class is an other concrete implementation of  AbstractFileReader. This class will decrypt the text data after reading and may utilise the SimpleFileReader to remove the extra white spaces.

public class EncryptFileReader extends AbstractFileReader{

public EncryptFileReader(Reader reader) {
super(reader);
}
@Override
public String read(){
String data = reader.read();
return decodeData(data);
}

private String decodeData(String data){
return data.replace("0", " ");
}

@Override
public String getDescription() {
return "Encrypt file reader" ;
}
}

How to test: Now we will write a demo class to test our decorator as following.

public class DecoratorDemo {

public static void main(String[] args){
        SimpleFileReader simpleFileReader = new SimpleFileReader(new InputReader("test.txt"));
      System.out.println(simpleFileReader.getDescription());
      System.out.println(simpleFileReader.read());//display the text after removing the white spaces
     
        System.out.println("---------------");
        
        EncryptFileReader fileReader = new EncryptFileReader( new SimpleFileReader(new InputReader("test.txt")));
      System.out.println(fileReader.getDescription());
      System.out.println(fileReader.read());//display the decoded text after removing the white spaces.
           
         /*even we can can do this using the Reader interface as following
        Reader reader = new EncryptFileReader( new SimpleFileReader(new InputReader("test.txt")));
        System.out.println(fileReader.read());   */
}
}

Output:


Simple file Reader
this0is01st0line
and
this0is02nd0line
---------------
Encrypt file reader
this is 1st line
and
this is 2nd line

Decorator Design Pattern used in java API

java.io.BufferedReader;
java.io.FileReader;
java.io.Reader;
and FileInputStream etc.
The above readers of java API are designed using decorator design pattern and are used as following.

BufferedReader reader = new BufferedReader(new FileReader(new File("C:\\testing.txt")));

No comments :

Post a Comment