Decorator (Dekoratör)

Introduktion

Decorator är ett strukturmönster som låter dig lägga till nytt beteende till ett objekt genom att linda in det i ett dekoratörobjekt. Det är ett flexibelt alternativ till arv för att utöka funktionalitet.


Problem

Du bygger ett notifieringssystem. Från början skickar det bara e-post. Snart vill användare också ha SMS-notifieringar. Sedan Slack. Sedan pushnotifieringar.

Du kan inte skapa en underklass för varje kombination — det skulle bli EmailAndSms, EmailAndSlack, EmailAndSmsAndSlack… En kombinatorisk explosion. Arv är fel verktyg här.

Lösning

Decorator lindar in ett objekt och lägger till beteende runt det befintliga. Dekoratörer implementerar samma gränssnitt som det objekt de lindar in och kan staplas hur som helst vid runtime.

När ska Decorator användas?

  • När du vill lägga till beteende till enskilda objekt utan att påverka andra instanser av samma klass.
  • När arv är opraktiskt på grund av för många kombinationer.
  • När du vill kunna kombinera beteenden fritt vid runtime.

Struktur

RollAnsvar
Component InterfaceDefinierar gränssnittet för både wrappade objekt och dekoratörer.
Concrete ComponentDet ursprungliga objektet med grundläggande beteende.
Base DecoratorHåller en referens till det wrappade objektet och delegerar till det.
Concrete DecoratorLägger till extra beteende före/efter delegationen.

Exempel — Notifieringssystem (C# / .NET)

Scenariot: En Notifier kan utökas med e-post, SMS och Slack-notifieringar — i valfri kombination — utan att ändra grundklassen.

// ── Component Interface ───────────────────────────────────

public interface INotifier
{
    void Send(string message);
}

// ── Concrete Component ────────────────────────────────────

public class BaseNotifier : INotifier
{
    private readonly string _recipient;

    public BaseNotifier(string recipient)
    {
        _recipient = recipient;
    }

    public void Send(string message) =>
        Console.WriteLine($"E-post till {_recipient}: {message}");
}

// ── Base Decorator ────────────────────────────────────────

public abstract class NotifierDecorator(INotifier wrapped) : INotifier
{
    protected readonly INotifier Wrapped = wrapped;

    public virtual void Send(string message) => Wrapped.Send(message);
}

// ── Concrete Decorators ───────────────────────────────────

public class SmsDecorator(INotifier wrapped, string phoneNumber) : NotifierDecorator(wrapped)
{
    public override void Send(string message)
    {
        base.Send(message);
        Console.WriteLine($"SMS till {phoneNumber}: {message}");
    }
}

public class SlackDecorator(INotifier wrapped, string channel) : NotifierDecorator(wrapped)
{
    public override void Send(string message)
    {
        base.Send(message);
        Console.WriteLine($"Slack #{channel}: {message}");
    }
}

public class PushDecorator(INotifier wrapped, string deviceToken) : NotifierDecorator(wrapped)
{
    public override void Send(string message)
    {
        base.Send(message);
        Console.WriteLine($"Push till enhet {deviceToken}: {message}");
    }
}

// ── Klientkod ─────────────────────────────────────────────

// Grundläggande e-postnotifiering
INotifier notifier = new BaseNotifier("victor@bytebase.se");

// Lägg till SMS
notifier = new SmsDecorator(notifier, "+46701234567");

// Lägg till Slack
notifier = new SlackDecorator(notifier, "ops-alerts");

// Lägg till pushnotifiering
notifier = new PushDecorator(notifier, "device-abc123");

// Ett anrop — alla kanaler notifieras
notifier.Send("Servern är nere!");

Output:

E-post till anna@bytebase.se: Servern är nere!
SMS till +46701234567: Servern är nere!
Slack #ops-alerts: Servern är nere!
Push till enhet device-abc123: Servern är nere!

Fördelar

  • Single Responsibility Principle — Varje dekoratör hanterar ett enda extra beteende.
  • Open/Closed Principle — Lägg till nya dekoratörer utan att ändra befintlig kod.
  • Kombinera beteenden fritt vid runtime — ingen kombinatorisk explosion av underklasser.

Nackdelar

  • En stack av många dekoratörer kan vara svår att felsöka.
  • Ordningen på dekoratörerna spelar roll och kan ge oväntade resultat.

Av Victor Hernandez från Bytebase.se