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