Il problema: due dimensioni di variazione che moltiplicano le classi
Hai un sistema di notifiche con due dimensioni di variazione: il tipo di notifica (urgente, normale, informativa) e il canale di invio (email, SMS, Slack, push). Con l'ereditarieta classica creeresti: UrgentEmailNotification, UrgentSmsNotification, UrgentSlackNotification, NormalEmailNotification, NormalSmsNotification... 3 tipi x 4 canali = 12 classi. Aggiungi un nuovo canale (Telegram) e servono 3 classi in più. Aggiungi un nuovo tipo (critica) e ne servono 4 in più.
Il Bridge Pattern risolve questa esplosione combinatoria separando le due dimensioni: l'astrazione (il tipo di notifica) e l'implementazione (il canale di invio). Invece di 12 classi, ne servono 3 + 4 = 7, e aggiungerne una in qualsiasi dimensione non richiede di toccare l'altra.
Cos'e il Bridge Pattern: definizione formale
Il Gang of Four definisce il Bridge come un pattern strutturale che "disaccoppia un'astrazione dalla sua implementazione in modo che le due possano variare indipendentemente". La struttura prevede:
-
Abstraction: la classe di alto livello che definisce il comportamento (es.
Notification) -
RefinedAbstraction: specializzazioni dell'astrazione (es.
UrgentNotification,NormalNotification) -
Implementor: l'interfaccia per il meccanismo di basso livello (es.
MessageSenderInterface) -
ConcreteImplementor: le implementazioni specifiche (es.
EmailSender,SmsSender,SlackSender)
L'Abstraction mantiene un riferimento all'Implementor e delega le operazioni di basso livello. Le due gerarchie evolvono indipendentemente.
Esempio teorico: rendering su piattaforme diverse
Un'applicazione genera grafici (chart) che possono essere visualizzati in formati diversi: SVG per il web, PNG per le email, PDF per i report. L'astrazione Chart ha sottoclassi BarChart, LineChart, PieChart. L'implementor RendererInterface ha SvgRenderer, PngRenderer, PdfRenderer.
Senza Bridge: 3 chart x 3 renderer = 9 classi. Con Bridge: 3 chart + 3 renderer = 6 classi. E ogni nuova combinazione e automatica: un LineChart con PdfRenderer funziona senza scrivere una sola riga di codice aggiuntivo.
Bridge nel mondo reale: driver di database
Il pattern Bridge e alla base di ogni sistema multi-driver. L'astrazione QueryBuilder definisce l'API fluente per costruire query (select(), where(), join()). L'implementor Grammar traduce l'albero della query in SQL specifico per il database. MysqlGrammar genera backtick e LIMIT, PgsqlGrammar genera double-quote e LIMIT con sintassi diversa, SqliteGrammar gestisce le limitazioni di SQLite.
In Soft PHP MVC, questa separazione e reale: il QueryBuilder costruisce la struttura logica della query, e la Grammar la traduce in SQL. Aggiungere un nuovo database richiede solo una nuova Grammar, senza toccare il QueryBuilder. Aggiungere un nuovo metodo al QueryBuilder funziona automaticamente su tutti i database.
Bridge vs Strategy: la differenza
- Strategy: cambia un singolo algoritmo a runtime. Il contesto ha un comportamento che può variare.
- Bridge: separa due gerarchie che evolvono indipendentemente. Entrambe le dimensioni possono avere sottoclassi.
Se hai una sola dimensione di variazione (es. l'algoritmo di ordinamento), usa Strategy. Se hai due dimensioni ortogonali che si combinano (es. tipo di notifica x canale di invio), usa Bridge.
Quando usare il Bridge Pattern
- Usa il Bridge quando hai due dimensioni di variazione indipendenti che si combinano
- Usa il Bridge quando vuoi evitare un'esplosione combinatoria di sottoclassi
- Usa il Bridge quando l'implementazione deve poter cambiare a runtime senza influenzare l'astrazione
- Non usare il Bridge se hai una sola dimensione di variazione: Strategy e più semplice
- Non usare il Bridge se le due dimensioni non variano mai indipendentemente: la separazione e prematura
Il Bridge Pattern e il pattern della separazione ortogonale: due assi di cambiamento che non si influenzano. Quando le riconosci nel tuo dominio, il Bridge trasforma un'esplosione di classi in una composizione lineare.
Top comments (0)