La programmazione orientata ad oggetti è un paradigma particolarmente adatto alla modellazione della realtà nel contesto di un software. Un vantaggio consiste nella modularità del codice che deriva dall'applicazione del modello con conseguente miglioramento nella gestione di manutenzione e sviluppo.
Nella progettazione di software sviluppato in ottica OOP ricorrono spesso paradigmi e relazioni noti tra i vari modelli. Alcuni dei paradigmi sono stati studiati e codificati per fornire una base teorica al loro utilizzo, identificandone vantaggi e svantaggi.
Nonostante nella maggior parte dei casi l'utilizzo dei pattern codificati equivalga all'adozione di buone prassi, è necessario evitare di over ingegnerizzare il codice per non complicarne la leggibilità. Dobbiamo cercare infatti di raggiungere un equilibrio tra necessità dell'applicazione e pattern che permettono di aumentare manutenibilità, testabilità e modularità .
Classificazione dei design pattern
I design pattern rispondono a problemi ricorrenti offrendo soluzioni riproducibili: essi sono stati classificati all'interno di diverse categorie da Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides, autori del libro "Design Patterns: Elements of Reusable Object-Oriented Software", il testo che ha contribuito alla diffusione di questi concetti.
Le tre categorie individuate da questi autori sono:
- pattern comportamentali;
- pattern creazionali;
- pattern strutturali.
I pattern comportamentali identificano soluzioni alle interazioni tra oggetti diversi rendendo l'implementazione irrilevante per l'utilizzatore che si riferisce esclusivamente ad una o più interfacce. Un esempio è dato dal pattern Iterator che permette di ottenere una serie di risultati da parte di un oggetto all'interno di un ciclo, senza conoscere da dove provenga o come sia mantenuta questa serie.
I pattern creazionali identificano modalità per istanziare oggetti allo scopo di rendere facilmente modificabile il processo di inizializzazione (ad esempio il pattern Factory) oppure per sostituire il comportamento di default del linguaggio (ad esempio il pattern Singleton). Il campo di applicazione più immediato è costituito dalla creazione di oggetti complessi dipendenti da altri oggetti senza che l'utilizzatore abbia conoscenza della fase d'istanza.
Infine i pattern strutturali hanno lo scopo di adattare il codice esistente a interfacce nuove e per loro natura sono i più versatili e utilizzati in ambito Web. Il vantaggio principale nell'utilizzo di questi pattern consiste nella possibilità di utilizzare librerie esterne in un progetto senza legare il codice utilizzatore a quella particolare libreria: per mantenere lo stesso funzionamento è sufficiente creare una nuova implementazione dell'interfaccia attesa con una libreria diversa.
Non bisogna però fare l'errore di considerare i pattern come un set definito e stabile nel tempo: oltre a quelli identificati dalla Gang of four, come vengono definiti i quattro autori del testo citato, esistono altri pattern introdotti successivamente. Inoltre la scelta di un pattern rispetto ad un altro che risolve un problema simile può essere dettata da ragioni contingenti non necessariamente stabili nel tempo: ad esempio per l'accesso ad una base dati i due pattern principali utilizzati sono Active Record e Repository, con il primo che ha avuto un enorme impatto sugli ORM di tutti i linguaggi di programmazione, spinto anche dalla sua adozione da parte di Rails, mentre il secondo sta guadagnando il favore degli sviluppatori per i vantaggi che comporta in termini di testabilità.
Applicazione dei concetti a PHP
PHP, nato come linguaggio puramente procedurale, si è evoluto notevolmente per quanto riguarda il supporto OOP, pur non raggiungendo, per il momento, il livello di altri linguaggi nativamente OOP. Ad oggi è possibile applicare la maggior parte dei pattern codificati allo sviluppo in PHP e i framework più utilizzati come Symfony e Laravel ne fanno uso.
I pattern su cui concentreremo l'attenzione nei prossimi capitoli sono quelli associati ad un'interfaccia: sono infatti le interfacce il concetto principale da comprendere per accedere ad uno studio proficuo dei pattern. In quest'ambito il loro funzionamento è il seguente: il nostro software definisce una serie di interfacce che rappresentano il contratto che è necessario rispettare per poter fornire una determinata funzione, quindi definiamo un'implementazione specifica che rispetti tale contratto.
I vantaggi principali nell'applicazione di queste tecniche sono:
Vantaggio | Descrizione |
---|---|
Testabilità | E' possibile sostituire interamente un'implementazione funzionale che utilizza un servizio esterno (un database, un filesystem esterno, un server SMTP) con un mockup che risponde in maniera prevedibile e indipendente da qualsiasi altro servizio permettendo di verificare il funzionamento esclusivo del nostro codebase senza incappare in errori dovuti a responsabilità esterne. |
Dipendenza debole da librerie specifiche | Di fatto l'utilizzo della libreria che esegue il compito per il quale è progettata viene limitato esclusivamente all'implementazione dell'interfaccia prevista permettendone la sostituzione con una nuova implementazione. |
Aggiunta di funzionalità | Permette di sovrapporre strati di oggetti che ne incapsulano altri, ad esempio un oggetto usato esclusivamente per interrogare un database al quale viene aggiunta la possibilità di effettuare il log delle interrogazioni. |
Semplificazione delle api di una libreria | Creazione di un'interfaccia limitata alle sole funzioni che effettivamente verranno utilizzate all'interno dell'applicazione. |