Skip to main content

Java Design Patterns: Decorator Pattern

Before talking about Decorator design pattern, think of decorators in real life. Let's consider a very common  example.

In an ice cream parlor you can order ice creams in Vanilla, Butter Scotch, Chocolate and Pineapple flavors with or without an assortment of toppings. Here are your choices:

  • Chocolate sauce
  • Strawberry sauce
  • Maple Syrup
  • Fruits and nuts
  • or just a Cherry
You can even do a mix and match of toppings according to your liking. So you can have a scoop (or two) of vanilla ice cream with Strawberry syrup topped with fruits and nuts and a cherry. 


Each of these toppings is a 'Decorator'. As evident from name itself, Decorators in real life 'decorate' an existing object. They ADD something to an existing object. Decorator design pattern also solves the same problem.

If you think of implementing this system, your first instinct would be to create a base class Icecream and make Vanilla, ButterScotch, Chocolate and Pineapple extend it. Correct.


public interface Icecream {
 String getDescription();
 double getCost();
}


public class ButterScotch implements Icecream{
  private final double cost = 125.90;

  public String getDescription(){
    return("This is ButterScotch icecream");
  }
  
  public double getCost(){
    return cost;
  } 
}


public class Vanilla implements Icecream{
  private final double cost = 75.90;

  public String getDescription(){
    return("This is Vanilla icecream");
  }
  
  public double getCost(){
    return cost;
  }
}




Now since a client may want any ice cream with or without one or more toppings, the behavior has to be decided on run time. You will notice that one or more decorators (toppings) come into picture now.

Decorator pattern is used when we have to attach additional responsibilities to an object at run time. Note the emphasis on 'object'. As I said earlier also, since there can be various combinations of toppings, behavior can be different for different objects. If you consider each ice cream as a different object, one of your ice creams could be vanilla with chocolate sauce while other could be pineapple with strawberry sauce. In both cases end product is an Icecream and it has a description and a cost, but obviously it will differ in both cases. Inheritance will apply to entire class hence it has to be ruled out.

To put simply, Decorator pattern makes use of wrapper classes which wrap the core object and 'decorate' core object by repeated invocations. 

Before we create individual wrapper classes for each topping, we create an abstract decorator class which implements the base interface.


abstract class IcecreamDecorator implements Icecream{
  abstract public String getDescription();  
  abstract public double getCost();
}

and then make concrete decorator class for each topping:


public class MapleSyrup extends IcecreamDecorator {
  
  Icecream icecream;
  public MapleSyrup(Icecream icecream){    
    this.icecream = icecream;
    System.out.println("Created Maple Syrup");
  }
  
  public String getDescription(){
    return(icecream.getDescription()+ " with Maple Syrup Topping");
  }
  
  public double getCost(){
    return icecream.getCost() + 25;
  }
}



Looking at code of MapleSyrup you should understand why did our IcecreamDecorator have to implement the base interface. Because both getDescription and getCost work on top of the object it is wrapping, concrete decorator class has to expose same properties as base class. If you can get description and cost of a plain vanilla ice cream, you should also be able to get description and cost of a vanilla ice cream topped with maple syrup.

Do you really need an interface as base class (Icecream)? No, it can as well be an abstract class if there are some methods that you will give a default implementation for and then override in your sub classes.

If your design is too simple you may feel that you can do away with decorator (in our case IcecreamDecorator) abstract class completely and have an instance of the base interface/abstract class (in our case Icecream) directly as an instance variable of your concrete decorator classes, something like this:


public class FruitAndNuts implements Icecream {
  Icecream icecream;
  
  public FruitAndNuts(Icecream icecream){
    this.icecream = icecream;
  }
  
  public String getDescription(){
    return icecream.getDescription()+" with Fruits and nuts";
  }
  
  public double getCost(){
    return icecream.getCost()+45;
  }
  
}


But this is bad design if you consider the fact that Icecream may undergo modification later. Maybe we add a  new method 'getStorageTemperature()' to it and then you MUST make a change to all these concrete decorator classes which is not really a good idea. Whereas having an abstract class in form of IcecreamDecorator, you can give a default implementation of getStorageTemperature()and then override it only when needed in your concrete decorator classes.

Before I wrap this post, here is the code to take all the above code for a spin:


public class IcecreamParlorClient {
  public static void main(String[] args){
    Icecream order1 = new Vanilla();
    System.out.println("Order1 description "+ order1.getDescription()+ " Order1 cost: "+order1.getCost());
    order1 = new MapleSyrup(order1);
    System.out.println("Order1 description "+ order1.getDescription()+ " Order1 cost: "+order1.getCost());
    order1 = new FruitAndNuts(order1);
    System.out.println("Order1 description "+ order1.getDescription()+ " Order1 cost: "+order1.getCost());
  }
}

Output:



Orde1 description This is Vanilla icecream Order1 cost: 75.9
Created Maple Syrup
Orde1 description This is Vanilla icecream with Maple Syrup Topping Order1 cost: 100.9
Created Fruits and Nuts
Orde1 description This is Vanilla icecream with Maple Syrup Topping with Fruits and nuts Order1 cost: 145.9

See how each time a new topping is being added, description and cost is being calculated on top of its wrapped object.

I hope this post cleared any doubts you may have had with this pattern.




Comments

Popular posts from this blog

How to upload to Google Cloud Storage buckets using CURL

Signed URLs are pretty nifty feature given by Google Cloud Platform to let anyone access your cloud storage (bucket or any file in the bucket) without need to sign in. Official documentation gives step by step details as to how to read/write to the bucket using gsutil or through a program. This article will tell you how to upload a file to the bucket using curl so that any client which doesn't have cloud SDK installed can do this using a simple script. This command creates a signed PUT URL for your bucket. gsutil signurl -c 'text/plain' -m PUT serviceAccount.json gs://test_bucket_location Here is my URL: https://storage.googleapis.com/test_sl?GoogleAccessId=my-project-id@appspot.gserviceaccount.com&Expires=1490266627&Signature=UfKBNHWtjLKSBEcUQUKDeQtSQV6YCleE9hGG%2BCxVEjDOmkDxwkC%2BPtEg63pjDBHyKhVOnhspP1%2FAVSr%2B%2Fty8Ps7MSQ0lM2YHkbPeqjTiUcAfsbdcuXUMbe3p8FysRUFMe2dSikehBJWtbYtjb%2BNCw3L09c7fLFyAoJafIcnoIz7iJGP%2Br6gAUkSnZXgbVjr6wjN%2FIaudXIqA

Running Apache Beam pipeline using Spark Runner on a local standalone Spark Cluster

The best thing about Apache Beam ( B atch + Str eam ) is that multiple runners can be plugged in and same pipeline can be run using Spark, Flink or Google Cloud Dataflow. If you are a beginner like me and want to run a simple pipeline using Spark Runner then whole setup may be tad daunting. Start with Beam's WordCount examples  which help you quickstart with running pipelines using different types of runners. There are code snippets for running the same pipeline using different types of runners but here the code is running on your local system using Spark libraries which is good for testing and debugging pipeline. If you want to run the pipeline on a Spark cluster you need to do a little more work! Let's start by setting up a simple standalone single-node cluster on our local machine. Extending the cluster is as easy as running a command on another machine, which you want to add to cluster. Start with the obvious: install spark on your machine! (Remember to have Java a

java.lang.IllegalArgumentException: Malformed \uxxxx encoding

I was getting this exception during build while running ant. Googling didn't help much and I was flummoxed because the same code was running fine till now. My code reads a text file and does some operations on the basis of values read. It was only when I saw the text files I understood the error. I had copied the text in wordpad and saved it as .txt file. Wordpad had put lot of formatting information before and after the content. Also there was "\par" after every line, which was giving this error. So moral of the story: if you get this exception check your properties file (or any other file that your code might be reading.)