Bird
Raised Fist0

Decorator Pattern - Adding Behavior to Objects Without Inheritance

LLDeasyInterview freq: common
Choose your preparation mode4 modes available
▶
Class Trace
Step 1/4
Component abstract class

Component is an abstract class defining the operation() method to be overridden by subclasses.

🎯 Tests whether candidate knows abstract class is used here instead of interface because Component may hold shared state or default behavior in Java.
public abstract class Component {
    public abstract void operation();
}
ConcreteComponent realization

ConcreteComponent extends Component and provides a concrete implementation of operation().

🎯 Tests understanding of realization inheritance and how concrete classes implement abstract methods in Java.
public class ConcreteComponent extends Component {
    @Override
    public void operation() {
        System.out.println("ConcreteComponent operation");
    }
}
Decorator composition

Decorator extends Component and holds a Component reference via composition (◆) to delegate calls.

🎯 Tests whether candidate understands composition lifecycle: Decorator owns the Component reference and delegates operation(), enabling dynamic behavior extension without inheritance.
public abstract class Decorator extends Component {
    protected Component component; // composition
    public Decorator(Component component) {
        this.component = component;
    }
    @Override
    public void operation() {
        component.operation();
    }
}
ConcreteDecoratorA extension

ConcreteDecoratorA extends Decorator and overrides operation() to add behavior before/after delegating to component.operation().

🎯 Tests Open-Closed Principle (OCP) in Java: extending behavior by subclassing Decorator without modifying existing classes, using inheritance and composition.
public class ConcreteDecoratorA extends Decorator {
    public ConcreteDecoratorA(Component component) {
        super(component);
    }
    @Override
    public void operation() {
        System.out.println("ConcreteDecoratorA before operation");
        super.operation();
        System.out.println("ConcreteDecoratorA after operation");
    }
}
📐
Class Diagram
Class Diagram
       +----------------+
       | <<Abstract>>   |
       | Component      |
       +----------------+
       | +operation()   |
       +----------------+
              ^
              | extends
       +---------------------+
       | ConcreteComponent    |
       +---------------------+
       | +operation()         |
       +---------------------+
              ^
              | extends
       +----------------+
       | Decorator      |
       +----------------+
       | -component:    |
       |   Component ◆  |
       | +operation()   |
       +----------------+
              ^
              | extends
       +---------------------+
       | ConcreteDecoratorA   |
       +---------------------+
       | +operation()         |
       +---------------------+

This class diagram illustrates the Decorator pattern in Java focusing on four classes: Component as an abstract class, ConcreteComponent as a concrete realization, Decorator using composition to hold a Component reference, and ConcreteDecoratorA extending Decorator to add behavior. The use of an abstract class for Component allows shared state or default methods, which interfaces cannot hold in Java. Decorator composes a Component, owning its lifecycle, enabling dynamic behavior extension without inheritance modification, adhering to the Open-Closed Principle. ConcreteDecoratorA overrides operation() to add pre- and post-processing around the delegated call, demonstrating how behavior can be layered transparently. The diagram uses inheritance (extends), composition (◆), and realization to clearly show relationships and lifecycle ownership.

Primary Focus

✓ Abstract class vs interface + Composition lifecycle + Decorator OCP + Realization inheritance

Practice

(1/5)
1. A class has a private constructor and a static synchronized getInstance() method that returns the single instance. Which design pattern is applied here, and why is this the correct use case?
easy
A. Factory Method pattern, because it controls object creation centrally.
B. Singleton pattern, ensuring only one instance exists and is thread-safe.
C. Builder pattern, to construct complex objects step-by-step.
D. Prototype pattern, to clone existing instances efficiently.

Solution

  1. Step 1: Identify the pattern from constructor and method signatures

    The private constructor prevents external instantiation, and the static synchronized getInstance() method ensures a single shared instance with thread safety.
  2. Step 2: Eliminate other creational patterns

    Factory Method creates objects but allows multiple instances; Builder constructs complex objects but doesn't restrict instance count; Prototype clones instances but doesn't enforce single instance.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Private constructor + synchronized static getInstance() -> Singleton pattern [check]
Hint: Private constructor + static getInstance() -> Singleton pattern [check]
Common Mistakes:
2. In designing a Vehicle abstraction, should it be an abstract class or an interface if it needs to hold state like speed and fuelLevel? Why?
easy
A. Interface, because Java 8+ interfaces can have default methods and fields.
B. Interface, because it allows multiple inheritance and enforces behavior contracts.
C. Abstract class, because it can hold shared state and provide default behavior implementations.
D. Abstract class, because it prevents any subclass from overriding methods.

Solution

  1. Step 1: Analyze state and behavior requirements

    Since Vehicle needs to hold mutable state like speed and fuelLevel, it requires fields and possibly default implementations that manage this state.
  2. Step 2: Compare abstract class vs interface capabilities

    Abstract classes can hold instance variables and provide default method implementations that access state. Interfaces cannot hold instance state (only constants) and default methods cannot manage mutable state effectively.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Stateful abstraction -> abstract class, not interface [check]
Hint: Stateful abstraction needs abstract class, not interface [check]
Common Mistakes:
3. The Meeting Scheduler currently calculates fees using a FeeCalculator class. To add a new WeekendFeeStrategy without modifying existing code, which SOLID principle applies and what is the minimum change required?
medium
A. Dependency Inversion Principle: inject WeekendFeeStrategy into FeeCalculator by modifying its constructor.
B. Open/Closed Principle: create a new WeekendFeeStrategy class implementing FeeStrategy interface; FeeCalculator uses it polymorphically.
C. Single Responsibility Principle: modify FeeCalculator to handle weekend fees directly.
D. Liskov Substitution Principle: subclass FeeCalculator and override fee calculation for weekends.

Solution

  1. Step 1: Identify SOLID principle for extensibility

    Open/Closed Principle (OCP) requires adding new behavior without modifying existing classes.
  2. Step 2: Apply Dependency Inversion Principle (DIP)

    Injecting the new WeekendFeeStrategy via constructor or setter allows FeeCalculator to depend on abstractions, enabling extension without modification.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Inject new strategy -> respects OCP and DIP [check]
Hint: Inject new strategy -> OCP + DIP respected [check]
Common Mistakes:
4. The current Order Management System uses a FeeStrategy interface for pricing. To add a WeekendFeeStrategy that applies extra fees on weekends, which minimal change respects SOLID principles?
medium
A. Create a WeekendFeeDecorator that wraps existing FeeStrategy implementations and adds weekend fees.
B. Add WeekendFeeStrategy as a new class implementing FeeStrategy and configure Order to use it without modifying existing classes.
C. Modify the existing FeeStrategy implementation to check for weekends and apply extra fees.
D. Add a boolean weekend flag to Order and modify FeeStrategy implementations to check it.

Solution

  1. Step 1: Identify SOLID principle involved

    Open-Closed Principle (OCP) requires adding new behavior without modifying existing code.
  2. Step 2: Evaluate options

    Add a boolean weekend flag to Order and modify FeeStrategy implementations to check it. modifies existing classes violating OCP; Modify the existing FeeStrategy implementation to check for weekends and apply extra fees. also modifies existing code; Add WeekendFeeStrategy as a new class implementing FeeStrategy and configure Order to use it without modifying existing classes. adds new class but requires Order modification; Create a WeekendFeeDecorator that wraps existing FeeStrategy implementations and adds weekend fees. adds decorator without modifying existing classes, respecting OCP and DIP.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Decorator adds behavior without modifying existing classes [check]
Hint: Decorator adds features without changing existing code [check]
Common Mistakes:
5. You need to add support for ElectricCar that requires ChargingSpot compatibility in the current parking system. What is the minimum change to the design to support this without modifying existing classes?
hard
A. Modify Vehicle class to add a boolean needsCharging and update parking logic to check this flag.
B. Add ELECTRIC_CAR to VehicleType enum, CHARGING to SpotType, create ElectricCar subclass, and update SpotCompatibility.COMPATIBILITY_MAP accordingly.
C. Create a new ElectricParkingLot subclass that handles electric cars and charging spots separately.
D. Add a ChargingSpot field to Vehicle and modify all parking methods to handle it.

Solution

  1. Step 1: Identify extensibility requirements

    ElectricCar and ChargingSpot are new types requiring compatibility mapping without changing existing classes.
  2. Step 2: Apply minimal invasive change

    Adding new enum values and subclass plus updating compatibility map extends functionality without modifying existing class code, preserving OCP.
  3. Step 3: Avoid violating OCP

    Options B, C, and D modify existing classes or create parallel hierarchies, increasing complexity and violating OCP.
  4. Final Answer:

    Option B -> Option B
  5. Quick Check:

    Extend enums and map, subclass ElectricCar -> minimal change [check]
Hint: Extend enums and map, subclass new type -> minimal change [check]
Common Mistakes:
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard