Decorator Design Pattern

Decorator Design Pattern is one of the Structural Design Pattern also know as Wrapper Design Pattern.

Decorator Design Pattern allows addition of features, functionality and modification on top of existing code at run time without changing existing structure.

Inheritance and Composition are used to add new functionality to the code but the limitation with this approach is that the addition of functionality is done at compile time only and cannot be changed at run time as per requirement. Decorator Design Pattern solves this issue.

Some real-world examples of Decorator Design Pattern are
There are different types of cars available in the market such as SUVs, Hatchbacks, Crossover, Convertible, Sedan, Sports Car, Coupe, Minivan, Station Wagon, and Pickup Truck. each type of car has its own uses, characteristics, attribute, and features, but one thing common is all of them is they have basic vehicle properties such as an engine, wheels and steering wheel. and the process of providing basic features is almost the same for all of them, then additional features can be added or removed as per the model and type of car;

All financial institutions provide OTP features for payment services on different channels such as SMS, call or email. some users opt for all the channels or can choose as per convenience such as SMS and email but not calls or SMS and call but not email or email and calls but no SMS. the more channels will be added then it becomes very hard to manage the flow as per the user requirement.

everyone loves pizza. it comes in multiple sizes and varieties, but the base of pizza remains the same irrespective of size and variety. then on top of it, new toppings or features can be added as per the requirement at run time.

Apple Salad is a healthy snack preferred by a lot of people and can be eaten in many ways. such as eating it raw, cutting it into small pieces, and adding seasoning species, all steps are independent of each other and can be added or removed as per the recipe.

package org.wesome.design.patterns;

import java.util.List;

public interface Fruit {
    String STEP_1 = "take raw apple";
    String STEP_2 = "cut apple in small pieces using slicer";
    String STEP_3 = "add seasoning using spice sprinkler";

    List<String> makeAppleSalad(List<String> steps);
}
package org.wesome.design.patterns;

import java.util.List;

public
class Apple implements Fruit {
    @Override
    public List<String> makeAppleSalad(List<String> steps) {
        steps.add(STEP_1);
        return steps;
    }
}
package org.wesome.design.patterns;

import lombok.AllArgsConstructor;

import java.util.List;

@AllArgsConstructor
public class AppleDecorator implements Fruit {
    protected Fruit fruit;

    @Override
    public List<String> makeAppleSalad(List<String> steps) {
        return this.fruit.makeAppleSalad(steps);
    }
}
package org.wesome.design.patterns;

import java.util.List;

public class AppleSeasoning extends AppleDecorator {
    public AppleSeasoning(Fruit c) {
        super(c);
    }

    @Override
    public List<String> makeAppleSalad(List<String> steps) {
        super.makeAppleSalad(steps);
        steps.add(STEP_3);
        return steps;
    }
}
package org.wesome.design.patterns;

import java.util.List;

public class AppleSlicer extends AppleDecorator {
    public AppleSlicer(Fruit c) {
        super(c);
    }

    @Override
    public List<String> makeAppleSalad(List<String> steps) {
        super.makeAppleSalad(steps);
        steps.add(STEP_2);
        return steps;
    }
}
package org.wesome.design.patterns;

import java.util.ArrayList;
import java.util.List;

public class Decorator {
    public static void main(String[] args) {
        List<String> steps = new ArrayList<>();
        Fruit appleSalad = new AppleSlicer(new Apple());
        steps = appleSalad.makeAppleSalad(steps);
        System.out.println(steps);
        steps.clear();

        appleSalad = new AppleSeasoning(new Apple());
        appleSalad.makeAppleSalad(steps);
        System.out.println(steps);
        steps.clear();

        appleSalad = new AppleSlicer(new AppleSeasoning(new Apple()));
        appleSalad.makeAppleSalad(steps);
        System.out.println(steps);
        steps.clear();

        appleSalad = new AppleSeasoning(new AppleSlicer(new Apple()));
        appleSalad.makeAppleSalad(steps);
        System.out.println(steps);
        steps.clear();
    }
}
package org.wesome.design.patterns;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static org.wesome.design.patterns.Fruit.STEP_1;
import static org.wesome.design.patterns.Fruit.STEP_2;
import static org.wesome.design.patterns.Fruit.STEP_3;

public class DecoratorTest {
    @Test
    void testDecorator() {
        List<String> actualSteps = new ArrayList<>();
        Fruit appleSalad = new AppleSlicer(new Apple());
        actualSteps = appleSalad.makeAppleSalad(actualSteps);
        List<String> expectedSteps = Arrays.asList(STEP_1, STEP_2);
        Assertions.assertEquals(expectedSteps, actualSteps);
    }

    @Test
    void testDecorator1() {
        List<String> actualSteps = new ArrayList<>();
        Fruit appleSalad = new AppleSeasoning(new Apple());
        appleSalad.makeAppleSalad(actualSteps);
        List<String> expectedSteps = Arrays.asList(STEP_1, STEP_3);
        Assertions.assertEquals(expectedSteps, actualSteps);
    }

    @Test
    void testDecorator2() {
        List<String> actualSteps = new ArrayList<>();
        Fruit appleSalad = new AppleSlicer(new AppleSeasoning(new Apple()));
        appleSalad.makeAppleSalad(actualSteps);
        List<String> expectedSteps = Arrays.asList(STEP_1, STEP_3, STEP_2);
        Assertions.assertEquals(expectedSteps, actualSteps);
    }

    @Test
    void testDecorator3() {
        List<String> actualSteps = new ArrayList<>();
        Fruit appleSalad = new AppleSeasoning(new AppleSlicer(new Apple()));
        appleSalad.makeAppleSalad(actualSteps);
        List<String> expectedSteps = Arrays.asList(STEP_1, STEP_2, STEP_3);
        Assertions.assertEquals(expectedSteps, actualSteps);
    }
}
plugins {
    id 'java'
    id "io.freefair.lombok" version "6.2.0"
}

group = 'org.wesome'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = JavaVersion.VERSION_1_8

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter:5.6.2'
}

test {
    useJUnitPlatform()
}

follow us on