Decorator Design Pattern

Reading Time: 3 minutes

Hi everyone!

In this blog, we are going to discuss decorator design patterns with Scala.

Let’s say I own a pizza outlet and we know that everyone has a very different taste and so there can be a various combination of toppings.

If I have n number of toppings, so i will have to create p(n) = 2 * p(n-1) + 1 Subclasses.

p(0) = 0

p(1) = 2 * p(1-1) + 1 = 1

p(2) = 2 * p(2-1) + 1 = 2 * p(1) + 1 = 2 * 1 + 1 = 3

p(3) = 2 * p2 + 1 = 2 * 3 + 1 = 7

p(4) = 2 * p3 + 1 = 2 * 7 + 1 = 15

So, if I have 3 toppings, the number of subclasses will be p(3) = 7, which is possible.

Wow!! ๐Ÿ˜ฎ My business is growing and now I want to expand it. So, I am going to add 2 more topping options for my valuable customers.

Now, when I have 5 toppings, the number of subclasses will be p(5) = 31. But wait!
It is really a very tedious task to be done๐Ÿ˜ž

So, what am I going to do now? ๐Ÿค”

Am I going to drop the idea of expanding the business?

Nahh! I am going to use the decorator design pattern to solve this problem.

What is a design pattern?

Design patterns are best practices that a programmer can use to solve the problems that a programmer commonly faces when designing an application or system,
i.e. we can say, it is a general and reusable solution to a commonly occurring problem.

It is not a finished design which can be transformed directly into the source code but it is a description or template for how to solve a problem that can be used in many different situations.

Decorator design pattern:

Decorator design pattern is a structural design pattern.

Structural design patterns focus on Class and Object composition and decorator design pattern is about adding responsibilities to objects dynamically.

Decorator design pattern gives some additional responsibility to our base class.

This pattern is about creating a decorator class that can wrap original class and can provide additional functionality keeping class methods signature intact.

It is somewhat like the chain of responsibility pattern with the difference that in the chain of responsibility pattern, exactly one of the classes handles the request, while in decorator design pattern, all classes handle the request.

A design that uses Decorator often results in a system composed of lots of little objects that all look alike.

Following will be the UML diagram if we follow the Decorator design pattern to solve our problem :

Decorator Design Pattern

Firstly, we have created Topping trait which is being implemented by classes BasePizza and ToppingDecorator and the Pizza class is composing it.
ToppingDecorator is further being extended by classes CheeseTopping and OnionTopping.

class BasePizza extends Topping {
def getName() : String = "Pizza"
def getPrice() : Double = 77.0
def addTopping() : Topping = this
}

view raw
BasePizza.scala
hosted with ❤ by GitHub

class CheeseTopping(override val topping : Topping) extends ToppingDecorator(topping) {
override def getPrice() : Double = {
super.getPrice() + 59.0
}
override def getName() : String = {
val previous = super.getName()
"Ocean Cheese " + previous
}
}

view raw
CheeseTopping.scala
hosted with ❤ by GitHub

class OnionTopping(override val topping : Topping) extends ToppingDecorator(topping) {
override def getPrice() : Double = {
super.getPrice() + 39.0
}
override def getName() : String = {
val previous = super.getName()
"Sprinkled Onion " + previous
}
}

view raw
OnionTopping.scala
hosted with ❤ by GitHub

class Pizza {
var topping : Topping = new BasePizza
def getPrice() : Double = {
topping.getPrice()
}
def getName() : String = {
topping.getName()
}
def addNewTopping(toppingName : String) : Boolean = {
toppingName match
{
case "Onion" =>
{
this.topping = new OnionTopping(topping)
true
}
case "Cheese" =>
{
this.topping = new CheeseTopping(topping)
true
}
case _ =>
println("Topping unavailable! Better luck next time! ๐Ÿ™")
false
}
}
}

view raw
Pizza.scala
hosted with ❤ by GitHub

object PizzaStore extends App {
val pizza = new Pizza
pizza.addNewTopping("Cheese")
pizza.addNewTopping("Onion")
println(s"You have ordered ${pizza.getName}")
println(s"You have to pay Rupees ${pizza.getPrice}")
}

view raw
PizzaStore.scala
hosted with ❤ by GitHub

trait Topping {
def getName() : String
def getPrice() : Double
def addTopping() : Topping
}

view raw
Topping.scala
hosted with ❤ by GitHub

class ToppingDecorator(val topping : Topping) extends Topping {
var nextTopping : Topping = topping
def getName() : String = nextTopping.getName()
def getPrice() : Double = nextTopping.getPrice()
def addTopping() : Topping = this
}

Hope you liked the blog. Thanks for reading!

References:

 


knoldus-advt-sticker

 

Written by 

Nancy jain is a software consultant with experience of more than 6 months. She likes to explore new technologies and trends in the IT world. Her hobbies include watching web series, writing and travelling. Nancy is familiar with programming languages such as Java, Scala, C, C++, HTML, Javascript and she is currently working on reactive technologies like Scala, DynamoDb, AkkaHttp.

5 thoughts on “Decorator Design Pattern2 min read

Comments are closed.

%d bloggers like this: