Decorator Design Pattern

Table of contents
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.



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


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



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


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
}
}



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


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
}
}



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


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



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


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}")
}



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


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

Topping.scala

hosted with ❤ by GitHub



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


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.

Discover more from Knoldus Blogs

Subscribe now to keep reading and get access to the full archive.

Continue reading