Introduzione
Nel lavoro quotidiano con PHP capita spesso di dover integrare librerie esterne, servizi API o classi legacy che non seguono la stessa interfaccia del nostro codice. In questi casi, invece di modificare tutto il sistema o scrivere logica di conversione sparsa ovunque, il Design Pattern Adapter ci permette di creare un ponte tra due componenti incompatibili.
In pratica, l’Adapter “adatta” una classe esistente a un’interfaccia attesa dal nostro codice. È un pattern molto utile quando vogliamo:
- integrare librerie di terze parti senza riscriverle;
- riutilizzare codice legacy con interfacce diverse;
- separare la logica di adattamento dal resto dell’applicazione;
- mantenere il codice più pulito, testabile e facile da sostituire.
Un esempio reale: il tuo sistema invia notifiche con un metodo send(), ma un provider esterno espone un metodo deliverMessage() con parametri diversi. L’Adapter risolve questa differenza senza costringerti a cambiare tutto il codice che già usa send().
Codice completo
<?php
declare(strict_types=1);
/**
* Interfaccia attesa dall´applicazione.
* Tutto il codice interno lavorerà con questa forma standard.
*/
interface NotificationSender
{
public function send(string $recipient, string $message): void;
}
/**
* Classe legacy o libreria esterna che NON possiamo modificare.
* Ha un´interfaccia diversa da quella che ci serve.
*/
class LegacySmsService
{
public function deliverMessage(string $phoneNumber, string $text): bool
{
echo "SMS inviato a {$phoneNumber}: {$text}" . PHP_EOL;
return true;
}
}
/**
* Adapter: rende compatibile LegacySmsService con NotificationSender.
*/
class SmsAdapter implements NotificationSender
{
public function __construct(
private LegacySmsService $legacySmsService
) {
}
public function send(string $recipient, string $message): void
{
// Adattamento dei nomi e del formato dei dati
$this- Spiegazione
Il codice mostra un caso molto comune: un servizio applicativo vuole inviare notifiche, ma i sistemi esterni hanno API diverse. Senza Adapter, il codice rischierebbe di essere pieno di if, conversioni manuali e dipendenze dirette verso classi esterne.
1. L’interfaccia comune
L’interfaccia NotificationSender rappresenta il contratto che il resto dell’applicazione si aspetta. È il punto di stabilità del sistema: qualsiasi classe che la implementa può essere usata da NotificationService.
2. La classe incompatibile
LegacySmsService e EmailApiClient simulano componenti esterni o vecchie classi già esistenti. Notiamo che i metodi non coincidono con l’interfaccia richiesta:
- deliverMessage() invece di send();
- un parametro singolo per SMS contro un array payload per email.
3. L’Adapter
SmsAdapter e EmailAdapter implementano l’interfaccia attesa dall’applicazione e traducono le chiamate nel formato corretto per il servizio reale. Questo è il cuore del pattern: convertire senza modificare il client.
Il vantaggio è importante: se domani cambiamo provider SMS o email, possiamo sostituire l’Adapter o crearne uno nuovo senza toccare NotificationService.
4. Il client applicativo
NotificationService non conosce i dettagli dei servizi esterni. Lavora solo con NotificationSender. Questo riduce l’accoppiamento e rende il codice più facile da testare, perché possiamo sostituire il sender con una finta implementazione nei test automatici.
Best practice
- Definisci un’interfaccia chiara: l’Adapter funziona meglio quando il tuo codice interno si appoggia a un contratto semplice e stabile.
- Limita la logica di conversione all’Adapter: evita di spargere trasformazioni di dati in più punti del progetto.
- Non usare l’Adapter come “contenitore di tutto”: dovrebbe tradurre interfacce, non diventare una classe con troppa responsabilità.
- Separa bene dominio e infrastruttura: il codice applicativo dovrebbe dipendere da astrazioni, non da dettagli esterni.
- Usa type hint e strict_types: in PHP moderno aiuta a intercettare errori prima e rende il contratto più leggibile.
- Scrivi test sull’interfaccia pubblica: testa NotificationService e gli Adapter, così puoi cambiare implementazione senza rompere il comportamento atteso.
Un errore comune è confondere Adapter con Facade. La Facade semplifica un sistema complesso offrendo un’interfaccia più comoda; l’Adapter invece rende compatibili due interfacce incompatibili. Nel dubbio, chiediti: sto semplificando o sto traducendo?
Riepilogo
Il Design Pattern Adapter è una soluzione elegante quando devi collegare componenti con interfacce diverse. In PHP è particolarmente utile con librerie esterne, servizi API e codice legacy. Grazie all’Adapter puoi:
- mantenere un’interfaccia interna coerente;
- isolare le differenze tra sistemi esterni;
- ridurre l’accoppiamento;
- facilitare test e manutenzione.
Se il tuo progetto cresce e integra sempre più servizi, l’Adapter diventa uno strumento fondamentale per evitare che l’incompatibilità tra API si trasformi in caos architetturale.
Approfondisci con risorse ufficiali
- PHP Manual - Classi, interfacce e type declarations: https://www.php.net/manual/it/
- PHP Manual - Strict typing: https://www.php.net/manual/it/functions.arguments.php#functions.arguments.type-declaration.strict
- Refactoring.Guru - Adapter Pattern: https://refactoring.guru/design-patterns/adapter
- PSR-4 Autoloading - Standard utile per organizzare classi e adapter in progetti PHP moderni: https://www.php-fig.org/psr/psr-4/
