Decorator Pattern
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å.”
- Når du vil kombinere features fleksibelt (fx logging + retry + kryptering)
- Når du ellers ville ende med “subklasse-eksplosion”:
EmailSenderWithLoggingEmailSenderWithLoggingAndRetryEmailSenderWithRetryAndEncryption- … 😵💫
Vi bruger samme idé som i Strategy-eksemplet: et fælles interface til at sende en besked.
public interface Sender {
void send(String message);
}
public class EmailSender implements Sender {
@Override
public void send(String message) {
System.out.println("EMAIL -> " + message);
}
}
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);
}
}
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!");
}
}
public class EmojiDecorator extends SenderDecorator {
public EmojiDecorator(Sender wrappee) {
super(wrappee);
}
@Override
public void send(String message) {
String withEmoji = "✨ " + message + " ✨";
super.send(withEmoji);
}
}
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);
}
}
}
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
- Samme interface hele vejen (
Sender) - Vi kan kombinere features uden if/else
- Vi kan tilføje nye decorators uden at ændre gamle klasser (OCP)
Lav en decorator der censurerer bestemte ord, før beskeden sendes.
- 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);
}
}
Sender sender =
new ProfanityFilterDecorator(
new EmailSender()
);
sender.send("Du er dum");
Forventet: EMAIL -> Du er ***
- 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)
Decorator Pattern = wrap objektet i lag af ekstra funktionalitet.