Decorator Pattern – extinderea funcționalității fără moștenire în .NET
În dezvoltarea modernă de software, flexibilitatea este esențială. Aplicațiile evoluează, cerințele se schimbă, iar componentele trebuie adaptate rapid fără a compromite stabilitatea sistemului. Decorator Pattern este unul dintre cele mai elegante și utile modele de design orientat pe obiecte, permițând extinderea comportamentului unui obiect în timpul rulării, fără a utiliza moștenire și fără a modifica structura existentă a clasei.
Acest pattern apare des în scenarii precum logarea, caching-ul, validarea, compresia sau criptarea fluxurilor. În .NET, Decorator este folosit chiar în framework-ul de bază, în special în sistemul de fluxuri (Stream, BufferedStream, GZipStream) și în middleware-urile ASP.NET Core.
1. Ce este Decorator Pattern?
Pe scurt, Decorator:
-
permite adăugarea de funcționalități în mod dinamic unui obiect existent;
-
evită moștenirea excesivă;
-
respectă principiul Open/Closed: clasa este deschisă pentru extindere, dar închisă pentru modificare;
-
menține aceeași interfață pentru obiectele decorate și cele de bază.
Decorator funcționează prin “îmbrăcarea” unui obiect existent într-un obiect suplimentar, care interceptează apelurile și introduce comportamente noi înainte sau după execuția reală.
2. Structura generală
Un Decorator clasic are trei componente:
-
Component – interfața comună
-
Concrete Component – implementarea principală
-
Decorator – clasa abstractă care implementează aceeași interfață și conține componentul decorat
-
Concrete Decorators – extensiile reale
3. Exemplu simplu în C#: extinderea unui serviciu de mesaje
Presupunem că avem un serviciu care trimite mesaje. Ulterior dorim să adăugăm logare și validare, fără să modificăm implementarea inițială.
Component
public interface IMessageService
{
void Send(string message);
}
Concrete Component
public class MessageService : IMessageService
{
public void Send(string message)
{
Console.WriteLine($"Sending message: {message}");
}
}
Decorator abstract
public abstract class MessageServiceDecorator : IMessageService
{
protected readonly IMessageService _inner;
protected MessageServiceDecorator(IMessageService inner)
{
_inner = inner;
}
public virtual void Send(string message)
{
_inner.Send(message);
}
}
Decorator pentru logare
public class LoggingDecorator : MessageServiceDecorator
{
public LoggingDecorator(IMessageService inner) : base(inner) { }
public override void Send(string message)
{
Console.WriteLine("[LOG] Trimitere mesaj...");
base.Send(message);
}
}
Decorator pentru validare
public class ValidationDecorator : MessageServiceDecorator
{
public ValidationDecorator(IMessageService inner) : base(inner) { }
public override void Send(string message)
{
if (string.IsNullOrWhiteSpace(message))
{
Console.WriteLine("Mesaj invalid!");
return;
}
base.Send(message);
}
}
Utilizare
IMessageService service =
new LoggingDecorator(
new ValidationDecorator(
new MessageService()));
service.Send("Salut din Decorator Pattern!");
Rezultatul:
[LOG] Trimitere mesaj...
Sending message: Salut din Decorator Pattern!
Observăm că funcționalitatea a fost extinsă fără moștenire multiplă sau modificarea implementării originale.
4. Avantaje ale Decorator Pattern
✔ extindere flexibilă și modulară
✔ evită clasele gigantice cu multe responsabilități
✔ permite combinarea comportamentelor în lanț
✔ funcționează excelent în .NET cu middleware, stream-uri, interceptoare
5. Când să folosim Decorator Pattern?
-
când vrei să adaugi funcționalități suplimentare fără a modifica clasa existentă;
-
când ai nevoie de combinarea unor comportamente în mod variabil;
-
când moștenirea duce la o ierarhie prea complicată;
-
când dorești un sistem extensibil în timp real.
6. Decorator în .NET din viața reală
Exemple foarte clare:
-
BufferedStream, GZipStream, CryptoStream → decorează Stream
-
Middleware în ASP.NET Core → fiecare componentă decorează request-ul
-
Decoratori în DI cu Scrutor → wrapping de servicii în runtime