Dato che Twitter Boostrap (v3) è il front-end framework più utlizzato, nonchè quello “installato” di default con la ZendSkeletonApplication vediamo come aggiornare il Navigation Menu Helper affinchè si adatti alla Navbar di Boostrap.

Dato che Twitter Boostrap (v3) è il front-end framework più utlizzato, nonchè quello “installato” di default con la ZendSkeletonApplication vediamo come aggiornare il Navigation Menu Helper affinchè si adatti alla Navbar di Boostrap.
Recentemente in un mio progetto con Zend Framework 1 ho implementato Pimple, il noto DIC progettato da Fabien Potencier.
Una delle lacune di ZF1, difatti, è la mancanza di un meccanismo interno di supporto alla Dependency injection.
In giro ci sono altri DIC ben costruiti, tuttavia la mia scelta e caduta su Pimple grazie alla sua semplicità di utilizzo ed alla sua robustezza (nochè alla fiducia sull’autore nonostante non sia un “Symfoniano”).
Tuttavia quando si lavora con Pimple ci si accorge subito di un problema, l’impossibilità di usare l’autocompletamento con l’IDE.
Difatti a causa dell’astrazione offerta dal DIC, l’IDE che sto usando (Netbeans) non capisce più quello che sta succedendo nel mio codice.
Non capisce ad esempio che $container['myService']
contiene un oggetto.
Questo per me è realmente un bel problema. Programmare senza suggerimenti sul codice e completamento automatico non è piacevole ed il mio IDE diventa inutile.
Oggi ho avuto la necessità di creare all’interno di una mia applicazione (sviluppata con Zend Framework 1) un URL composto da due parole.
Si tratta sicuramente di uno dei design pattern fondamentali introdotti dalla GoF.
Come il factory method rientra nella categoria dei pattern creazionali, cioè tra i modelli che forniscono meccanismi per la creazione di oggetti.
AbstractFactory
per creare i prodotti concreti.AbstractProduct
e definisce l’oggetto prodotto che deve essere creato dalla factory concreta corrispondente (ConcreteFactory
).AbstractFactory
e AbstractProduct
.Riassumendo, la responsabilità della creazione dei prodotti (ConcreteProduct
) è dei ConcreteFactory
, mentre AbstractFactory
funge soltanto da interfaccia per questi.
Supponiamo che abbiamo varie famiglie di prodotti… il nostro scopo sarà quello di sostituire facilmente una famiglia con un’altra evitando una chiamata esplicita ai costruttori delle classi prodotto.
Possiamo raggiungere tale obiettivo rendendo astratto il processo di creazione degli oggetti.
Prendiamo in considerazione il diagramma del pattern, ci sono due famiglie di prodotti la famiglia1 (ProductA1
e ProductB1
) e la famiglia2 (ProductA2
e ProductB2
).
Il nostro obiettivo quindi sarà quello, previa configurazione del sistema con una famiglia prodotto, di renderlo indipendente da come gli oggetti vengono creati.
Rappresenta l’interfaccia utilizzata dai client per creare i prodotti concreti.
I client utilizzeranno questa interfaccia per creare famiglie di oggetti connessi tra loro in modo che non gli venga richiesto di specificare esplicitamente il nome delle classi concrete all’interno del proprio codice.
1 2 3 4 5 6 7 8 |
interface AbstractFactory { //AbstractFactory public function createProductA(); public function createProductB(); } |
Implementano l’interfaccia AbstractFactory
e servono per creare i prodotti concreti. Ogni ConcreteFactory
sarà responsabile della creazione di una famiglia di prodotti ed avrà tanti metodi quanti sono i prodotti concreti da creare.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
//ConcreteFactory (fabbrica per la famiglia 1) class ConcreteFactory1 implements AbstractFactory { public function createProductA() { return new ProductA1(); } public function CreateProductB() { return new ProductB1(); } } //ConcreteFactory (fabbrica per la famiglia 2) class ConcreteFactory2 implements AbstractFactory { public function createProductA() { return new ProductA2(); } public function CreateProductB() { return new ProductB2(); } } |
Dichiara l’interfaccia per i prodotti concreti. I client utilizzeranno questa interfaccia indipendentemente dalla famiglia di prodotto con cui stiamo lavorando.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//AbstractProduct interface AbstractProductA { public function foo(); } //AbstractProduct interface AbstractProductB { public function baz(); } |
Rappresenta un prodotto concreto, cioè l’oggetto che abbiamo bisogno di istanziare attraverso le fabbriche. Naturalmente ogni ConcreteProduct
dovrà implementare l’interfaccia AbstractProduct
corrispondente.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
//Famiglia1 class ProductA1 implements AbstractProductA { public function foo() { echo 'Prodotto A famiglia 1'; } } class ProductB1 implements AbstractProductB { public function baz() { echo 'Prodotto B famiglia 1';; } } //Famiglia2 class ProductA2 implements AbstractProductA { public function foo() { echo 'Prodotto A famiglia 2'; } } class ProductB2 implements AbstractProductB { public function baz() { echo 'Prodotto B famiglia 2'; } } |
Infine vediamo come il client utilizzerà questa struttura.
Come detto il nostro obiettivo sarà quello di rendere il sistema indipendente dalla creazione degli oggetti (prodotti) al fine permettere un’agile sostituzione di una famiglia di prodotti con un’altra.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//client //instanzio la fabbrica $factory = new ConcreteFactory1(); //creo i prodotti $productA = $factory->createProductA(); $productB = $factory->CreateProductB(); $productA->foo(); $productB->baz(); //cambio famiglia $factory = new ConcreteFactory2(); $productA = $factory->createProductA(); $productB = $factory->CreateProductB(); $productA->foo(); $productB->baz(); |
L’esempio, anche se completamente privo di senso, ci dimostra come sia semplice passare da una famiglia ad un’altra, e come per il client non faccia nessuna distinzione la creazione di un oggetto sia quello di una famiglia piuttosto che di un’altra.
Senza il pattern se avessimo voluto creare il prodottoA
della famiglia1 avremmo utilizzato il seguente codice:
1 2 3 |
$productA = new ProductA1(); |
Questo è proprio quello che vogliamo evitare, al fine di minimizzare la dipendenza con un particolare tipo di famiglia.
Con il nostro approccio:
1 2 3 |
$productA = $factory->createProductA(); |
abbiamo evitato la chiamata diretta al costruttore di ProductA1
ottenendo lo stesso risultato ma con una differenza sostanziale.
L’oggetto factory
astrae completamente il processo di creazione non solo del prodotto A della famiglia1, ma per qualsiasi prodotto A, di qualsiasi famiglia.
Non solo, factory
non è limitata al prodotto A ma può gestire la creazione dell’intero set di prodotti.
Possiamo raggiungere questo risultato grazie al fatto che factory
implementa l’interfaccia AbstractFactory
. AbstractFactory
implementa difatti tutti i metodi necessari alla creazione delle diverse tipologie di prodotto (createProductA()
, createProductB()
).
Una delle domande ricorrenti circa questo pattern è quella di stabilire in che punto dobbiamo istanziare la factory
concreta. Quelli della banda dei quattro suggeriscono l’utilizzo del pattern singleton di modo da avere una sola istanza della fabbrica condivisa tra gli ambienti che la utilizzano.
Tuttavia nel corso degli anni abbiamo imparato come il pattern singleton sia considerato una cattiva pratica e possibilmente da evitare.
Quindi la risposta è: dipende dal contesto. Tuttavia deve essere fatto ragionevolmente presto e comunque prima che il programma abbia bisogno di usarla per la creazione dei prodotti.
Il pattern Abstract Factory è un modello creazionale, utilizzato per costruire oggetti.
Tutti i linguaggi OO hanno un idioma per la creazione di oggetti. In PHP l’idioma è l’operatore new
.
I pattern creazionali ci permettono di scrivere metodi che creano nuovi oggetti senza utilizzare esplicitamente tale operatore.
Questo ci porta ad uno dei principali vantaggi del pattern, e cioè che il client è totalmente disaccoppiato dai prodotti concreti.
Una volta inizializzata la fabbrica, saremo sicuri che l’applicazione sarà in grado di creare gli oggetti (prodotti) appropriati senza bisogno di modifiche.
Inoltre, possono essere aggiunte facilmente al sistema nuove famiglie di prodotti, semplicemente aggiungendo un nuovo tipo di ConcreteFactory
che implementa AbstractFactory
, e creando le specifiche implementazioni del prodotto.
Anche il factory method è un pattern avente come obiettivo la creazione di prodotti tuttavia l’abstract factory è particolarmente indicato quando il sistema deve creare più famiglie di prodotti, di cui ne sarà utilizzata solo una alla volta.
Nell’object-oriented programming il pattern Decorator è un design pattern strutturale, che ci permette di aggiungere funzionalità ad un oggetto dinamicamente in fase di runtime.
Detto anche Wrapper è di uno dei pattern fondamentali definiti dalla GoF.
Si tratta probabilmente di uno dei pattern (comportamentali) più famosi tra quelli teorizzati dalla banda dei quattro sul loro libro.
Il modello ha come scopo, una volta individuata una famiglia di algoritmi, di incapsularli rendendoli intercambiabili.
Il Composite è un pattern fondamentale definito dalla GoF e rappresenta un modo semplice di aggregazione e gestione dei gruppi di oggetti simili in modo che per un client un singolo oggetto sia indistinguibile da un insieme di oggetti.
Uno dei principi cardine della programmazione ad oggetti è sicuramente:
Programmare verso l’interfaccia non verso l’implementazione.
(Program to an interface, not an implementation)
Una delle attività fondamentali in un progetto orientato agli oggetti è stabilire rapporti tra le classi. Due modi di mettere in relazione le classi sono l’ereditarietà e la composizione.
Questo post ha come obiettivo mettere a confronto i precedenti due approcci ed eventualmente cercare di capire se uno è preferibile all’altro.
Non vi è dubbio che l’ereditarietà (detta anche specializzazione) è un potente strumento di progettazione per mutare le circostanze o i contesti. Si tratta di un meccanismo supportato direttamente dal linguaggio, il che si traduce in una notevole semplicità nel creare gerarchie di classi.
Tuttavia si dovrebbe anche considerare che l’ereditarietà crea una relazione statica definita a compile-time ed infrange il principio dell’incapsulamento creando una forte dipendenza nelle gerarchie.
Pro:
Contro:
L’ultimo punto è particolarmente “pericoloso”. Se si modifica la classe padre si rischia di dover riadattare anche tutte le classi figlio. Una teoria diffusa è quella di ereditare unicamente da classi astratte.
[box type=”note”]Da ricordare che in PHP (a differenza di altri linguaggi OOP) una classe può avere un solo genitore, non è supportata alcuna forma di eredità multipla.[/box]
Come abbiamo visto, la relazione di ereditarietà rende difficile cambiare l’interfaccia di una classe padre, dato che questo si ripercuoterà su tutte le classi figlio che andrebbero riadattate. Vale quindi la pena guardare un approccio alternativo, fornito dalla composizione.
La composizione prevede un approccio che rende più facile la modifica del codice.
La composizione ci offre un grande vantaggio, l’oggetto componente è accessibile unicamente attraverso la sua interfaccia, in questo modo non si viola il principio dell’incapsulamento.
Pro:
Contro:
Dal punto di vista della dipendenza possiamo anche affermare che la composizione porta alla creazione di due oggetti, mentre attraverso l’ereditarietà l’oggetto in esecuzione è uno solo.
Sia l’ereditarietà che la composizione sono due tecniche comuni riguardo al riuso di funzionalità (di codice quindi) nei sistemi ad oggetti.
Il riutilizzo attraverso le sotto classi è definito white-box. Il termine si riferisce alla visibilità del codice. Come detto le classi figlio nella maggior parte dei casi sono in grado di vedere la struttura interna delle classi padre, violando così il principio dell’incapsulamento.
Il riuso definito attraverso la composizione e detto black-box. Gli oggetti appaiono come scatole nere, i dettagli implementativi non sono visibili ma e visibile soltanto l’interfaccia.
Dal punto di vista didattico/teorico si dovrebbe sceglie tra ereditarietà e composizione in base alle relazioni che vengono espresse.
In una relazione is-a si dovrebbe preferire l’ereditarietà.
In una relazione has-a invece sarebbe preferibile la composizione.
Quindi se:
B
è un (is-a) oggetto di A
-> ereditarietà.B
ha un (has-a) oggetto di A
-> composizione.Ancora:
A
devono essere metodi anche di B
allora si usa l’ereditarietà.A
non devono essere metodi anche di B
allora si usa la composizione.Vediamo ora l’ereditarietà al lavoro, l’impiegato è una persona ed eredita da questa:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class Person { private $_firstName; private $_lastName; private $_age; public function getFullname() { return $this->_firstName . ' ' . $this->_lastName; } } class Employee extends Person { private $_company; private $_salary; } |
Tuttavia, fregandocene della più logica relazione is-a, ecco come possiamo spostare il precedente esempio a favore delle composizione:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
class Person { private $_firstName; private $_lastName; private $_age; public function getFullname() { return $this->_firstName . ' ' . $this->_lastName; } } class Employee { private $_person; private $_company; private $_salary; public function __construct(Person $person) { $this->_person = $person; } public function getFullName() { $this->_person->getFullname(); } } |
In questo esempio l’impiegato non eredita da una persona ma ottiene invece un oggetto Person
passato tramite il costruttore (Dependency Injection). Dal punto di vista delle relazioni l’impiegato non “è una” persona ma “ha una” persona.
[box type=”note”]Il meccanismo che fa si che la composizione diventi un’alternativa all’ereditarietà riguardo al riuso del codice è detto Delega. Con la delega, come abbiamo visto, l’oggetto che riceve la richiesta delaga la risposta al proprio delegato (nell’esempio Employee
delega la risposta a Person
).[/box]
Sia composizione che ereditarietà sono due “meccanismi” strettamente legati al tentativo di risolvere il problema del riutilizzo del codice (evitandone cioè la duplicazione).
Se il tuo intento e’ quello di riutilizzare parte del codice di una classe, invece che estenderla, sarebbe meglio utilizzare la composizione includendo nell’interfaccia solo i metodi per cui vuoi
delegarne l’implementazione.
Personalmente ho imparato a preferire quasi sempre la composizione rispetto all’ereditarietà. Non c’è nessun problema di programmazione risolvibile con l’eredità che non si possa risolvere con la composizione.
La composizione offre una migliore astrazione, migliore incapsulamento, elimina un sacco di complessità e diminuisce drasticamente la probabilità di rompere qualcosa da qualche parte quando si esegue una modifica nel codice.
Non ci sono spetti negativi nella composizione ed esclusione del fatto che saremo costretti a scrivere qualche riga in più di codice.
L’ereditarietà con il passare del tempo tende elevare la complessità, il codice diviene difficile da cambiare, mantenere, e scalare senza errori.
Quindi:
Il problema della duplicazione del codice è sicuramente alla base delle migliori tecniche di programmazione. Il codice duplicato rende la manutenzione del software più difficile, perché ogni codice duplicato deve essere mantenuto costante, e un bug che è stato trovato sul codice duplicato non può essere fixato in un solo punto.