Problem:

Let us assume that we are building a simple design to get the price of different pizzas in a pizza shop. Here, we have a number of pizzas to take into account various type and overlapping toppings. How can we implement the design?

One way to do that is by using traditional inheritance. We will create a base Pizza class and multiple subclasses for different Pizza combinations with a reference of toppings in it.

Now let’s find what are the glitches here:

  1. For each subclass of Pizza, it has to have Topping references. for example, A thin crust pizza can have Tomato and corn OR can have all 3.
  2. If we want to add new toppings to a particular Pizza, we need to alter the particular class code and again recompile it.
  3. Each subclass should have the Cost() calculation logic with respect to each topping’s cost.

From the above points, it is clear that Inheritance is not feasible as it is static and applies to an entire class. We use inheritance or composition to extend the behavior of an object but this is done at compile time and its application to all the instances of the class. We can’t add any new functionality to remove any existing behavior at runtime — this is when Decorator pattern comes into the picture.

What is Decorator?

Decorator patterns simply allow you to dynamically add functionality to a specific object instead of an entire class of objects.

Decorators provide a flexible alternative to subclassing for extending functionality.

This pattern creates a decorator class which wraps the original class and provides additional functionality keeping class methods signature intact.

Don’t worry, let’s understand it through code. Just keep this Data model in your mind.

The code segments are written in Apex.

Pizza class:

public abstract class Pizza{
String description = 'Pizza ';
//pizza cost
public abstract double cost();

//Pizza Description
public virtual String description(){
return description;
}
}

Thin Crust Pizza:

public class ThinCrustPizza extends Pizza{public override String description(){
return 'Thin Crust';
}

public override double cost(){
return 200.0;
}
}

Flat Bread Pizza:

public class FlatBreadPizza extends Pizza{public override String description(){
return 'Flat Bread';
}

public override double cost(){
return 200.0;
}
}

Now, up to here, everything is cool. We have created 2 pizzas. Here we have overcome the top 2 problems:

  1. Each subclass doesn’t have the toppings reference.
  2. Since there is no reference, Each Pizza is independent of adding or removing the toppings.

But they are incomplete without toppings.

In Inheritance, we are using composition to add toppings but here we create a decorator class that wraps the original class and changes its behavior at run time.

for example, If I want to add Corn to my pizza, it would look something like below image

Quite confusing???.

It’s Okay, let’s complete the whole structure and you will understand everything.

Topping class (The Decorator class)

public abstract class Topping extends Pizza{
public override abstract String description();
}

Onion:

public class Onion extends Topping{Pizza pizza;public Onion(Pizza pizza){
this.pizza = pizza;
}

public override String description(){
return this.pizza.description() + ', Onion';
}
public override double cost(){
return this.pizza.cost() + 15.0;
}
}

Cheese:

public class Cheese extends Topping{Pizza pizza;public Cheese(Pizza pizza){
this.pizza = pizza;
}

public override String description(){
return this.pizza.description() + ', Cheese';
}
public override double cost(){
return this.pizza.cost() + 20.0;
}
}

Corn:

public class Corn extends Topping{Pizza pizza;public Corn(Pizza pizza){
this.pizza = pizza;
}

public override String description(){
return this.pizza.description() + ', Corn';
}
public override double cost(){
return this.pizza.cost() + 30.0;
}
}

our Data model changes to something as below

Okay, So the code is done, the Data model is also changed, but still, I am not able to understand it.

Let’s execute it, It would be more clear.

I ordered One Pizza.

I want to add Corn.

I want to add Cheese too.

So here, our third problem is also solved. Since the cost function gets calculated dynamically, we are free to add new toppings, without altering the code pizza class logic.

Still confused, Let’s execute it programmatically :

OrderPizza class:

public class OrderPizza{

public OrderPizza(String pizzaName){
this(pizzaName,false,false,false);
}

public OrderPizza(String pizzaName, Boolean addCheese, Boolean addCorn, Boolean addOnion){
Pizza pizza = null;
if(pizzaName == 'Thin Crust'){
pizza = new ThinCrustPizza();
}
if(pizzaName == 'Flat Bread'){
pizza = new FlatBreadPizza();

}
if(pizza != null){
displayItems(pizza);
if(addCheese){
pizza = new Cheese(pizza);
displayItems(pizza);
}

if(addCorn){
pizza = new Corn(pizza);
displayItems(pizza);
}

if(addOnion){
pizza = new Onion(pizza);
displayItems(pizza);
}

}

}

void displayItems(Pizza pizza){
System.debug('Added '+pizza.description()+ ', Total is Rs. '+pizza.cost());
}
}

Input: (For Java, call it in Main)

new OrderPizza(‘Flat Bread’,false,true,true);

Output :

Source:

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store