Implementation

In Flutter community, it is quite popular to create food delivery/restaurant type of applications. With the implementation of the Decorator design pattern, we will jump into this hype train and will build a prototype for the pizza delivery application, to be more specific, for the pizza selection from the menu.

Let’s say we have a small restaurant which makes 3 kinds of pizza:

Margherita — Sauce, Mozzarella, Basil, Oregano, Pecorino, Olive Oil;

Pepperoni — Sauce, Mozzarella, Pepperoni, Oregano;

“Make-Your-Own” — any combination of pizza toppings from the list of Basil, Mozzarella, Olive Oil, Oregano, Pecorino, Pepperoni, and Sauce.

All the pizzas are of the same size, pizza toppings have different prices.

It is quite clear for the pizza Margherita or Pepperoni — the recipe is clear, you just need to add the necessary toppings and calculate the final price, easy peasy. However, for the custom pizza, it would be very impractical to prepare the pre-defined recipes for all the possible combinations — that’s just not how it works usually from the business point of view.

For this problem, the Decorator design pattern is a great option since we can make the pizza toppings as separate decorator classes, use them to wrap the pizza base (the base component) and calculate the final price of the pizza based on the selected toppings. Let’s check the class diagram first and then implement the pattern.

Class diagram

The class diagram below shows the implementation of the Decorator design pattern:

Class Diagram — Implementation of the Decorator design pattern

Pizza defines a common interface for wrappers (decorators) and wrapped objects:

getDescription() — returns the description of the pizza;

getPrice() — returns the price of the pizza.

PizzaBase represents the component object which extends the Pizza class and implements its abstract methods.

PizzaDecorator references the Pizza object and forwards requests to it via the getDescription() and getPrice() methods.

Basil, Mozzarella, OliveOil, Oregano, Pecorino, Pepperoni and Sauce are concrete decorators extending the PizzaDecorator class and overriding its default behaviour by adding some extra functionality/calculations of their own.

PizzaToppingData class stores information about the pizza topping’s selection chip used in the UI — its label and whether it is selected or not.

PizzaMenu class provides a getPizzaToppingsDataMap() method to retrieve the pizza topping’s selection chip data. Also, getPizza() method is defined to return the specific Pizza object based on the selected index in the UI or the selected pizza toppings.

DecoratorExample initialises and contains the PizzaMenu class object to retrieve the selected Pizza object based on the user’s selection in the UI.

Pizza

An abstract class of the Pizza component which defines a common interface for concrete component and decorator objects.

PizzaBase

A concrete component which extends the Pizza class and implements its methods. An object of this class (its behaviour) gets decorated by the specific decorator classes.

PizzaDecorator

An abstract decorator class which maintains a reference to a component class and forwards requests to it.

Concrete pizza decorators

Basil, Mozzarella, OliveOil, Oregano, Pecorino, Pepperoni and Sauce are concrete decorator classes of the Pizza component. Each of these classes wraps the pizza object and adds additional value for the final price in the getPrice() method, also extends the final pizza’s description in the getDescription() method.

Basil:

Mozzarella:

Olive Oil:

Oregano:

Pecorino:

Pepperoni:

Sauce:

PizzaToppingData

A simple class which contains data used by the pizza topping’s selection chip in the UI. The data consists of the label property and the current selection state (whether the chip is currently selected or not) which could be changed by using the setSelected() method.

PizzaMenu

A simple class which provides a map of PizzaToppingData objects via the getPizzaToppingsDataMap() method for the pizza toppings selection in UI. Also, the class defines a getPizza() method which returns a Pizza object that is built by using the pre-defined concrete decorator classes based on the pizza recipe — Margherita, Pepperoni or custom (based on the selected pizza toppings).

This class (to be more specific, getMargherita(), getPepperoni() and getCustom() methods) represents the main idea of the decorator design pattern — a base component class is instantiated and then wrapped by the concrete decorator classes, hence extending the base class and its behaviour. As a result, it is possible to use wrapper classes and add or remove responsibilities from an object at runtime, for instance, as it is used in the getCustom() method where the appropriate decorator classes are used based on the selected pizza toppings data in the UI.

Example

First of all, a markdown file is prepared and provided as a pattern’s description:

DecoratorExample contains the PizzaMenu object which is used to get the specific Pizza object based on the user’s selection. Also, all the logic related to the decorator’s design pattern and its implementation is extracted to the PizzaMenu class, the DecoratorExample widget only uses it to retrieve the necessary data to be represented in the UI.

The final result looks like this:

As you can see in the example when selecting any of the pre-defined recipes, the final price of the pizza is recalculated as well as the description of its toppings is provided. Also, for the custom pizza, the price is recalculated every time a topping is selected or deselected, the pizza’s description is updated, too.

All of the code changes for the Decorator design pattern and its example implementation could be found here.