Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Understanding the Decorator Pattern with Phone Renovation Example

Tech Aug 20 17

Overview

The Decorator Pattern enables dynamic extension of an object's functionality without altering its structure. This pattern offers two primary implementation strategies:

Approach 1: Extend the original class through inheritance to add supplementary features while preserving existing functionality.

Approach 2: Create a decorator wrapper class that encapsulates the original object and provides additional behavior while maintaining the original interface contract.

This article demontsrates both approaches using a practical scenario: renovating an old phone into a new one.

Approach 1: Inheritance-Based Decoration

Defining the Base Abstract Class

/**
 * Abstract base class representing a legacy device
 */
public abstract class LegacyDevice {

    /**
     * Renovation operation: transforming old into new
     */
    public abstract void renovate();
}

First-Level Decoration: Replacing the Shell

/**
 * Concrete decorator: device with new exterior
 */
public class RenovatedDeviceTier1 extends LegacyDevice {

    public void replaceShell() {
        System.out.println("Replacing external shell");
    }

    @Override
    public void renovate() {
        this.replaceShell();
    }
}

Second-Level Decoration: Components and Packaging

Building upon the first level, this class adds component replacement and repackaging:

/**
 * Advanced renovation: components and packaging
 */
public class RenovatedDeviceTier2 extends RenovatedDeviceTier1 {

    public void replaceComponents() {
        System.out.println("Replacing internal components");
    }

    public void updatePackaging() {
        System.out.println("Updating product packaging");
    }

    @Override
    public void renovate() {
        this.replaceComponents();
        super.replaceShell();
        this.updatePackaging();
    }
}

Testing the Inheritance Approach

/**
 * Verification test for inheritance-based decoration
 */
public class Application {

    public static void main(String[] args) {
        LegacyDevice device1 = new RenovatedDeviceTier1();
        device1.renovate();
        System.out.println("Market Value: $200");

        System.out.println("");

        LegacyDevice device2 = new RenovatedDeviceTier2();
        device2.renovate();
        System.out.println("Market Value: $350");
    }
}

Approach 2: Wrapper-Based Decoration

This approach uses composition rather than inheritance, providing greater flexibility.

Defining the Base Interface

/**
 * Contract for device renovation operations
 */
public abstract class Device {

    /**
     * Transformation from old to new state
     */
    public abstract void transform();
}

Concrete Component: Basic Renovated Device

/**
 * Basic renovation implementation
 */
public class BasicRenovation extends Device {

    public void replaceShell() {
        System.out.println("Replacing external shell");
    }

    @Override
    public void transform() {
        this.replaceShell();
    }
}

Abstract Decorator Class

/**
 * Base decorator providing wrapper functionality
 */
public abstract class DeviceDecorator extends Device {

    protected Device wrappedDevice;

    public DeviceDecorator(Device device) {
        this.wrappedDevice = device;
    }

    @Override
    public void transform() {
        this.wrappedDevice.transform();
    }
}

Concrete Decorators: Independent Operations

Each decorator handles a specific enhancement independently:

/**
 * Decorator for component replacement
 */
public class ComponentReplacement extends DeviceDecorator {

    public ComponentReplacement(Device device) {
        super(device);
    }

    public void replaceParts() {
        System.out.println("Replacing internal components");
    }

    @Override
    public void transform() {
        super.transform();
        this.replaceParts();
    }
}
/**
 * Decorator for packaging updates
 */
public class PackagingUpdate extends DeviceDecorator {

    public PackagingUpdate(Device device) {
        super(device);
    }

    public void refreshPackage() {
        System.out.println("Refreshing product packaging");
    }

    @Override
    public void transform() {
        super.transform();
        this.refreshPackage();
    }
}

Testing the Wrapper Approach

/**
 * Verification test for wrapper-based decoration
 */
public class Application {

    public static void main(String[] args) {
        Device device = new BasicRenovation();
        
        device = new ComponentReplacement(device);
        device = new PackagingUpdate(device);

        device.transform();
        System.out.println("Market Value: $350");
    }
}

Comparison

Aspect Inheritance Approach Wrapper Approach
Flexibility Limited by class hierarchy Runtime composition
Code Reuse Requires deep inheritance Independent decorators
Modification Requires new subclasses Add new decorators freely
Coupling Tight coupling to parent Loose coupling

The wrapper-based approach provides superior flexibility for dynamic feature composition, while inheritance offers simplicity for static hierarchies.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

SBUS Signal Analysis and Communication Implementation Using STM32 with Fus Remote Controller

Overview In a recent project, I utilized the SBUS protocol with the Fus remote controller to control a vehicle's basic operations, including movement, lights, and mode switching. This article is aimed...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.