Skip to main content
Dat 2. semester Bornholm
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Decorator Pattern

Decorator Pattern

Formål

Decorator Pattern bruges til at tilføje ekstra funktionalitet til et objekt uden at ændre objektets klasse – og uden at lave en masse subklasser.

Det gør man ved at wrappe (pakke) et objekt ind i et andet objekt, som har samme interface, men tilføjer noget før/efter kaldet.

“Jeg vil have samme objekt – bare med ekstra features ovenpå.”


Hvornår giver Decorator mening?

  • Når du vil kombinere features fleksibelt (fx logging + retry + kryptering)
  • Når du ellers ville ende med “subklasse-eksplosion”:
    • EmailSenderWithLogging
    • EmailSenderWithLoggingAndRetry
    • EmailSenderWithRetryAndEncryption
    • … 😵‍💫

Eksempel: “Send besked” med ekstra features

Vi bruger samme idé som i Strategy-eksemplet: et fælles interface til at sende en besked.

1) Interface

public interface Sender {
    void send(String message);
}

2) Basis-implementation (kernefunktion)

public class EmailSender implements Sender {
    @Override
    public void send(String message) {
        System.out.println("EMAIL -> " + message);
    }
}

3) Decorator-basisklasse

Decoratoren holder en reference til et andet Sender-objekt og kalder videre til det.

public abstract class SenderDecorator implements Sender {
    protected final Sender wrappee;

    protected SenderDecorator(Sender wrappee) {
        this.wrappee = wrappee;
    }

    @Override
    public void send(String message) {
        wrappee.send(message);
    }
}

4) Konkrete decorators

LoggingDecorator

public class LoggingDecorator extends SenderDecorator {

    public LoggingDecorator(Sender wrappee) {
        super(wrappee);
    }

    @Override
    public void send(String message) {
        System.out.println("[LOG] about to send: " + message);
        super.send(message);
        System.out.println("[LOG] sent!");
    }
}

EmojiDecorator (lille “sjov” feature)

public class EmojiDecorator extends SenderDecorator {

    public EmojiDecorator(Sender wrappee) {
        super(wrappee);
    }

    @Override
    public void send(String message) {
        String withEmoji = "✨ " + message + " ✨";
        super.send(withEmoji);
    }
}

RetryDecorator (simpel version)

public class RetryDecorator extends SenderDecorator {

    public RetryDecorator(Sender wrappee) {
        super(wrappee);
    }

    @Override
    public void send(String message) {
        try {
            super.send(message);
        } catch (RuntimeException ex) {
            // super simpel retry (kun for demo)
            System.out.println("[RETRY] trying again...");
            super.send(message);
        }
    }
}

5) Brug Decorator (stack dem!)

Sender sender =
        new RetryDecorator(
            new LoggingDecorator(
                new EmojiDecorator(
                    new EmailSender()
                )
            )
        );

sender.send("Velkommen til kurset!");

Output (idéen):

  • Emoji tilføjes
  • Logging før/efter
  • Retry hvis der kommer en exception

Hvad har vi opnået?

  • Samme interface hele vejen (Sender)
  • Vi kan kombinere features uden if/else
  • Vi kan tilføje nye decorators uden at ændre gamle klasser (OCP)

Opgave: “ProfanityFilterDecorator” (bandeord-filter)

Lav en decorator der censurerer bestemte ord, før beskeden sendes.

Krav

  • Klassen skal hedde ProfanityFilterDecorator
  • Den skal arve fra SenderDecorator
  • Den skal erstatte ord som "dum" og "idiot" med "***"

Skeleton:

public class ProfanityFilterDecorator extends SenderDecorator {

    public ProfanityFilterDecorator(Sender wrappee) {
        super(wrappee);
    }

    @Override
    public void send(String message) {
        // TODO: censor words
        super.send(message);
    }
}

Test-scenarie

Sender sender =
        new ProfanityFilterDecorator(
            new EmailSender()
        );

sender.send("Du er dum");

Forventet: EMAIL -> Du er ***


Factory vs Strategy vs Decorator (hurtigt overblik)

  • Factory: “Hvilket objekt skal jeg have?” (oprettelse)
  • Strategy: “Hvordan skal det gøres?” (udskiftelig adfærd)
  • Decorator: “Samme objekt, men med ekstra features ovenpå.” (kombinér ansvar)

Kort opsummering

Decorator Pattern = wrap objektet i lag af ekstra funktionalitet.