In this lesson, we'll step into the world of the Decorator pattern. The Decorator pattern is a structural design pattern that allows you to dynamically add behavior to an object without altering its structure. This pattern is particularly useful when you want to add responsibilities to objects without subclassing.
In this lesson, you will master:
Let's see the Decorator Pattern in action through a practical example.
To understand the Decorator pattern, we'll use an example involving different types of cars and decorations (or features) that can be dynamically added to them. In our example, the objective is to create customizable cars with dynamic features such as a sports car and a luxury car.
Car
object and implements the Car
interface.Decorator
class to add specific features to the Car
.First, we define the Car
interface, which declares the core method for assembling a car.
Java1public interface Car { 2 String assemble(); 3}
In this example, Car
is the component interface that declares the assemble
method. All decorator classes will implement this interface.
Next, we create the BasicCar
class that implements the Car
interface.
Java1public class BasicCar implements Car { 2 @Override 3 public String assemble() { 4 return "Basic Car"; 5 } 6}
Here, BasicCar
provides the base functionality by implementing the assemble
method to return the string "Basic Car."
The CarDecorator
class implements the Car
interface and contains a reference to a Car
object.
Java1public class CarDecorator implements Car { 2 private Car decoratedCar; 3 4 public CarDecorator(Car car) { 5 this.decoratedCar = car; 6 } 7 8 @Override 9 public String assemble() { 10 return decoratedCar.assemble(); 11 } 12}
The CarDecorator
class acts as a wrapper for a Car
object, delegating the call to the assemble
method of the wrapped object.
The concrete decorators extend the CarDecorator
class to add specific features.
Java1public class SportsCar extends CarDecorator { 2 public SportsCar(Car car) { 3 super(car); 4 } 5 6 @Override 7 public String assemble() { 8 return super.assemble() + " + Sports Car Features"; 9 } 10}
In the SportsCar
class, we extend CarDecorator
and add sports car features to the assemble
method.
Java1public class LuxuryCar extends CarDecorator { 2 public LuxuryCar(Car car) { 3 super(car); 4 } 5 6 @Override 7 public String assemble() { 8 return super.assemble() + " + Luxury Car Features"; 9 } 10}
Similarly, the LuxuryCar
class extends CarDecorator
and adds luxury car features to the assemble
method.
Here's how you can use the SportsCar
and LuxuryCar
decorators to add features to a BasicCar
.
Java1public class Main { 2 public static void main(String[] args) { 3 Car sportsCar = new SportsCar(new BasicCar()); 4 System.out.println(sportsCar.assemble()); // Outputs: Basic Car + Sports Car Features 5 6 Car luxuryCar = new LuxuryCar(new BasicCar()); 7 System.out.println(luxuryCar.assemble()); // Outputs: Basic Car + Luxury Car Features 8 } 9}
In the Main
class, we create a BasicCar
and then wrap it with SportsCar
and LuxuryCar
decorators to dynamically add their respective features.
The Decorator pattern is crucial in software development for several reasons:
By mastering the Decorator pattern, you can design more adaptable and maintainable systems. It encourages the development of flexible code that can evolve with changing requirements.
Ready to solidify your understanding with some hands-on practice? Let's dive into the practice section!