javaintermediate

Decorator Pattern — OOP and Functional

Implement the decorator pattern both classically and with functional composition for flexible behavior.

java
import java.util.function.*;

// === Classical OOP approach ===
interface Notifier {
    void send(String message);
}

class EmailNotifier implements Notifier {
    public void send(String message) {
        System.out.println("Email: " + message);
    }
}

abstract class NotifierDecorator implements Notifier {
    protected final Notifier wrapped;
    NotifierDecorator(Notifier wrapped) { this.wrapped = wrapped; }
}

class SlackDecorator extends NotifierDecorator {
    SlackDecorator(Notifier wrapped) { super(wrapped); }
    public void send(String message) {
        wrapped.send(message);
        System.out.println("Slack: " + message);
    }
}

class SmsDecorator extends NotifierDecorator {
    SmsDecorator(Notifier wrapped) { super(wrapped); }
    public void send(String message) {
        wrapped.send(message);
        System.out.println("SMS: " + message);
    }
}

// === Functional approach ===
public class DecoratorDemo {

    // Function decorators
    static <T, R> Function<T, R> withLogging(Function<T, R> fn, String name) {
        return input -> {
            System.out.printf("[%s] input: %s%n", name, input);
            R result = fn.apply(input);
            System.out.printf("[%s] output: %s%n", name, result);
            return result;
        };
    }

    static <T, R> Function<T, R> withTiming(Function<T, R> fn) {
        return input -> {
            long start = System.nanoTime();
            R result = fn.apply(input);
            long ms = (System.nanoTime() - start) / 1_000_000;
            System.out.printf("Took %d ms%n", ms);
            return result;
        };
    }

    static <T, R> Function<T, R> withRetry(Function<T, R> fn, int maxRetries) {
        return input -> {
            Exception last = null;
            for (int i = 0; i <= maxRetries; i++) {
                try { return fn.apply(input); }
                catch (Exception e) { last = e; }
            }
            throw new RuntimeException("Failed after " + maxRetries + " retries", last);
        };
    }

    public static void main(String[] args) {
        // OOP: stack decorators
        Notifier notifier = new SmsDecorator(
            new SlackDecorator(
                new EmailNotifier()
            )
        );
        notifier.send("Server is down!");

        // Functional: compose decorators
        Function<String, String> process = String::toUpperCase;
        var decorated = withTiming(withLogging(process, "upper"));
        System.out.println(decorated.apply("hello world"));
    }
}

Use Cases

  • Adding behavior to objects without subclassing
  • Composable middleware-style processing
  • Notification systems with multiple channels

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.