Wednesday, 1 January 2014

Decorator Design pattern - Implementation [Dosa]

🥞 Master Core Java & Structural Design Patterns!

Enjoying this hands-on breakdown? Subscribe to Ram N Java for clean code examples, software design principles, and beginner-friendly tutorials.

🔔 Subscribe to Ram N Java

Understanding the Decorator Design Pattern

The Decorator Pattern is a structural design pattern that lets you attach additional responsibilities or enhancements to an existing object dynamically at runtime without altering its original code.

Instead of relying on deep inheritance trees—which creates class explosion—this pattern uses object composition to wrap objects inside flexible decorators.

The Real-World Scenario: Decorating a Dosa

Consider ordering breakfast at an Indian restaurant. Everything begins with a standard Plain Dosa. From there, you might want delicious variations such as:

  • Masala Dosa (Plain Dosa decorated with potato masala)
  • Onion Dosa (Plain Dosa decorated with finely chopped onions)

Creating rigid sub-classes for every conceivable combination creates unnecessary complexity. With the Decorator Pattern, decorators wrap the base plain dosa to add masala or onions dynamically!

Step-by-Step Code Walkthrough

1. Component Interface

Define the core contract for making a dosa:

public interface Dosa {
    String makeDosa();
}

2. Concrete Component

Implement the base plain dosa without any toppings:

public class PlainDosa implements Dosa {
    @Override
    public String makeDosa() {
        return "Plain Dosa";
    }
}

3. Abstract Decorator

The decorator class implements Dosa and holds a reference to a wrapped Dosa object:

public abstract class DosaDecorator implements Dosa {
    protected Dosa dosa;

    public DosaDecorator(Dosa dosa) {
        this.dosa = dosa;
    }

    @Override
    public String makeDosa() {
        return dosa.makeDosa();
    }
}

4. Concrete Decorators

Create specialized decorators for masala and onion toppings:

public class MasalDosaDecorator extends DosaDecorator {
    public MasalDosaDecorator(Dosa dosa) {
        super(dosa);
    }

    @Override
    public String makeDosa() {
        return dosa.makeDosa() + " + Masala added";
    }
}

public class OnionDosaDecorator extends DosaDecorator {
    public OnionDosaDecorator(Dosa dosa) {
        super(dosa);
    }

    @Override
    public String makeDosa() {
        return dosa.makeDosa() + " + Onion added";
    }
}

5. Decorator Client Test

public class DecoratorClient {
    public static void main(String[] args) {
        // Plain Dosa
        Dosa plainDosa = new PlainDosa();
        System.out.println(plainDosa.makeDosa());

        // Onion Dosa
        Dosa onionDosa = new OnionDosaDecorator(new PlainDosa());
        System.out.println(onionDosa.makeDosa());

        // Masala Dosa
        Dosa masalDosa = new MasalDosaDecorator(new PlainDosa());
        System.out.println(masalDosa.makeDosa());
    }
}

Why Use the Decorator Pattern?

  • Open/Closed Principle: Add new toppings easily without changing existing classes.
  • Runtime Flexibility: Assemble and combine features on demand.
  • Clean Modularity: Prevents massive, unmaintainable class hierarchies.

Related Tutorials You Might Like

Deepen your software engineering expertise with these design pattern tutorials:



Click here to watch in Youtube : https://www.youtube.com/watch?v=x5mIx0Wq-nI


Click the below Image to Enlarge

Decorator Design pattern - Implementation [Dosa]


Decorator Design pattern - Implementation [Dosa] - Class Diagram




































Dosa.java

public interface Dosa
{
public String makeDosa();
}

PlainDosa.java

public class PlainDosa implements Dosa
{

@Override
public String makeDosa()
{
return "Plain Dosa ";
}
}

DosaDecorator.java

abstract class DosaDecorator implements Dosa
{

protected Dosa dosa;

public DosaDecorator( Dosa dosa )
{
this.dosa = dosa;
}

public String makeDosa()
{
return dosa.makeDosa();
}
}

MasalaDosaDecorator.java

public class MasalaDosaDecorator extends DosaDecorator
{

public MasalaDosaDecorator( Dosa dosa )
{
super(dosa);
}

public String makeDosa()
{
return dosa.makeDosa() + addMasala();
}

private String addMasala()
{
return ",Masala added";

}
}

OnionDosaDecorator.java

public class OnionDosaDecorator extends DosaDecorator
{

public OnionDosaDecorator( Dosa dosa )
{
super(dosa);
}

public String makeDosa()
{
return dosa.makeDosa() + addOnion();

}

private String addOnion()
{
return ",Onion added";
}
}

DecoratorClient.java

public class DecoratorClient
{

public static void main( String args[] )
{
PlainDosa plainDosaObj = new PlainDosa();
String plainDosa = plainDosaObj.makeDosa();
System.out.println(plainDosa);

String onionDosa = new OnionDosaDecorator(plainDosaObj).makeDosa();
System.out.println("\n'" + onionDosa + "' using OnionDosaDecorator");

String masalaDosa = new MasalaDosaDecorator(plainDosaObj).makeDosa();
System.out.println("\n'" + masalaDosa + "' using MasalaDosaDecorator");

}

}

Output

Plain Dosa 

'Plain Dosa ,Onion added' using OnionDosaDecorator

'Plain Dosa ,Masala added' using MasalaDosaDecorator

See also:

  • Decorator Design pattern - Introduction
  • Decorator Design pattern - Real Time Example [Dosa]
  • Decorator Design pattern - Real Time Example [Ice Cream]
  • Decorator Design pattern - Real Time Example [Pizza]
  • Decorator Design pattern - Real Time Example [Car]
  • Decorator Design pattern - Class Diagram
  • Decorator Design pattern - Sequence Diagram
  • Decorator Design pattern - Implementation [Pizza]
  • Decorator Design pattern - Implementation [Ice Cream]
  • Decorator Design pattern - Implementation [Car]
  • Decorator Design pattern - Implementation [Shape]
  • Decorator Design pattern - Key Points
  • All Design Patterns Links
  • No comments:

    Post a Comment

    Tutorials