Adapter Pattern – compatibilitatea între clase cu interfețe diferite
În dezvoltarea software, lucrăm adesea cu librării, servicii sau componente care nu au fost gândite să funcționeze împreună. Fie au interfețe diferite, fie expun metode incompatibile cu nevoile aplicației. Aici intervine Adapter Pattern, un design pattern structural esențial, care permite integrarea a două clase altfel incompatibile, fără a modifica implementările acestora.
Dacă un Factory te ajută să creezi obiecte și un Builder te ajută să le compui, Adapter îți permite să folosești obiecte existente într-un nou context. De aceea este unul dintre cele mai folosite pattern-uri în proiecte enterprise, API integrations și refactorizări.
🧩 Ce este Adapter Pattern?
Adapter este un obiect intermediar care transformă interfața unei clase într-o alta, așteptată de client.
Astfel, clientul nu știe și nu trebuie să știe că folosește un obiect diferit — totul se face transparent, prin adaptare.
Scop: să facă două clase cu interfețe incompatibile să colaboreze.
Acest pattern apare frecvent atunci când:
-
integrezi o librărie externă (care nu poate fi modificată),
-
convertești formate diferite,
-
refactorizezi o aplicație veche pentru a lucra cu o arhitectură nouă,
-
conectezi diverse surse de date (API, XML, JSON, BBDD, microservicii etc.).
🔧 Exemplu aplicat în .NET (C#)
Să presupunem că ai un sistem care lucrează cu o interfață:
public interface IEmailSender
{
void SendEmail(string to, string message);
}
Dar ai o librărie externă care expune altă metodă:
public class ExternalMailService
{
public void Send(string destination, string body)
{
Console.WriteLine($"Mail trimis către {destination}: {body}");
}
}
Cele două nu sunt compatibile. Totuși, vrei să folosești librăria externă în sistemul tău.
Aici intervine Adapter-ul:
public class EmailSenderAdapter : IEmailSender
{
private readonly ExternalMailService _externalService;
public EmailSenderAdapter(ExternalMailService service)
{
_externalService = service;
}
public void SendEmail(string to, string message)
{
_externalService.Send(to, message); // adaptare simplă
}
}
Acum clientul folosește sistemul standard:
IEmailSender emailSender = new EmailSenderAdapter(new ExternalMailService());
emailSender.SendEmail("test@example.com", "Salut!");
Fără să știe că în spate se folosește altceva — exact esența Adapter Pattern.
🏗 Tipuri de Adapter
Există două variante principale:
1. Class Adapter (bazat pe moștenire)
Folosește inheritance. Mai rar folosit în C#, deoarece C# nu permite moștenire multiplă.
Poate fi întâlnit doar dacă adapți o singură clasă concretă.
2. Object Adapter (bazat pe compoziție)
Cel mai folosit în .NET.
Adapterul conține instanța obiectului adaptat și îl folosește intern.
Avantaj major:
✔ funcționează cu orice implementare, atâta timp cât îi este furnizată în constructor.
🧠 Avantaje ale Adapter Pattern
-
Permite reutilizarea codului existent.
-
Evită modificarea claselor care nu pot fi schimbate (API extern, legacy).
-
Favorizează arhitecturile flexibile (SOLID, în special Open/Closed).
-
Ușurează tranziția între sisteme vechi și noi.
⚠️ Dezavantaje
-
Introduce un nivel suplimentar de abstractizare.
-
Poate complica structura dacă ai prea multe adaptări.
-
Dacă ai de adaptat funcționalitate complexă, poate deveni greu de întreținut.
🏁 Concluzie
Adapter Pattern este unul dintre cele mai practice și des utilizate pattern-uri în .NET.
Ori de câte ori ai două sisteme cu interfețe diferite, îl poți folosi pentru a face integrarea simplă, clară și scalabilă, reducând nevoia de modificare în codul existent.
În proiectele enterprise, adaptarea este constantă: API-uri vechi, microservicii noi, biblioteci externe. De aceea, Adapter rămâne un instrument esențial în arsenalul oricărui dezvoltator C#.
Dacă vrei să construiești cod curat, extensibil și ușor de întreținut, acest pattern merită să devină o obișnuință.
