Nella scorsa
puntata abbiamo visto come i pattern siano "una soluzione a un problema
in un contesto". Ma ogni pattern è un buon pattern? No, certo. Un
buon pattern deve risolvere il problema più importante di un contesto.
Quale è il problema più importante del software? Il riuso.
Quindi il requisito fondamentale a cui deve rispondere un pattern è:
produrre codice riusabile.
Sui Pattern
Abbiamo detto
che un pattern è una soluzione assodata a un problema in un contesto.
Ciò vuol dire che i pattern limitano la creatività individuale?
Affatto! Dice Alexander:
"Un pattern è
allo stesso tempo una cosa che accade nel mondo, la regola che ci dice
come creare quella cosa, quando crearla. È insieme un processo e
una cosa; insieme la descrizione di una cosa che è viva e la descrizione
del processo che genera quella cosa". Questa frase esprime pienamente l’aspetto
dinamico, generativo dei pattern.
"Ci saranno sempre
variazioni e unicità nel modo in cui un pattern manifesta se stesso
[sottolineato nell’originale]. Ogni pattern è una soluzione generica
a un qualche sistema di forze nel mondo. Ma le forze non sono mai le stesse.
Siccome l’esatta configurazione dei dintorni in un certo posto e in un
certo momento è sempre unica, la configurazione delle forze cui
è sottoposto il sistema è anche unica... ne segue che anche
il sistema deve essere unico; non può essere esattamente uguale
a nessun altro, anche se è approssimativamente simile. Questa non
è una conseguenza accidentale: è un aspetto essenziale della
vita e dell’essere ogni parte in rapporto con il tutto (wholeness)" [Alex79,
p. 147].
La qualità
senza nome
Prosegue Alexander:
"In breve, c’è una caratteristica nelle cose naturali che è
creata dal fatto che sono riconciliate con le loro forze interne". Più
avanti Alexander la chiama "qualità senza nome" (the quality without
a name), per sottolineare che la si può vivere (come nel concetto
tedesco di erlebniss), ma non la si può definire. La qualità
senza nome è quella qualità "negli edifici e nelle città
che non può essere fatta, ma solo generata, indirettamente, dalle
ordinarie azioni delle persone, come un fiore non può essere fatto,
ma solo generato dal seme". La qualità senza nome è insieme
brutale utilitarismo, piacevolezza, robustezza al cambiamento e olismo
(integrazione della parte nel tutto). Ma, come nota Alexander, che ogni
tentativo di definire o misurare la Qualità è futile, perché
"le parole confondono più di quanto spieghino".
L’accenno al
seme ricorda due cose: una buona architettura non nasce già fatta,
ma è frutto del lento adattamento di un’idea embrionale al contesto
e ai suoi problemi; inoltre, una buona architettura ha la "qualità
della natura", è una "cosa viva", cresce quasi da sola. Uno dei
segni più indicativi del fatto che si è trovata l’architettura
giusta per un problema è quando l’architettura stessa suggerisce
le soluzioni ai problemi prima ancora che si presentino. Come dice Coplien
[Cope97], "un buon progetto anticipa ciò che cambierà e ciò
che resterà lo stesso nel tempo".
Un pattern non
è solo la descrizione di una soluzione ricorrente. "I pattern nel
mondo meramente esistono. Ma gli stessi pattern nella nostra mente sono
dinamici. Hanno forza. Sono generativi. Ci dicono che cosa fare; ci dicono
come potremmo, o dovremmo, generarli; ci dicono anche che, in determinate
circostanze, dobbiamo crearli" [Cope96].
Dire che un
pattern è composto da un contesto, un problema e una soluzione non
dice solo che si può usare quel pattern come soluzione a un problema.
Dice anche che è desiderabile farlo [Alex79, pag. 183]: il pattern
dice che in quel contesto è naturale che si presenti un certo problema.
Anche per questo un buon pattern genera le soluzioni ai problemi che si
presentano.
Se voglio sapere
quali sono i problemi tipici nel progetto di software real-time il testo
migliore è un libro di pattern per real-time; un libro sul real-time
non impostato a pattern fornirà una libreria di astute ottimizzazioni
e sapienti tecniche di sincronizzazione, lasciando nell’imbarazzo di capire
cosa convenga usare per questo specifico problema (per inciso: un libro
può essere impostato a pattern anche senza dirlo esplicitamente,
e un libro non è ottimo solo perché nel titolo c’è
scritto "Pattern").
Se non si può
definire la qualità senza nome, si può almeno capire che
domande porsi, che cammino seguire per perseguirla? Sì, si può.
Un pattern è la soluzione di un problema. Per definire un pattern
bisogna sapere quale è il problema, il vero problema.
Poiché
i pattern sono soluzioni a problemi ricorrenti, dobbiamo domandarci quale
siano i veri problemi ricorrenti del software.
Pattern e
riuso
Quali sono i
problemi più ricorrenti del software? Il problema più lamentato
da chi scrive il software è che ne scrive troppo. Fine ultimo del
programmatore è di scrivere meno software. Non tutti sono d’accordo
con questa definizione; chi vi propone un tool mirabolante che "aumenta
la produttività" evidentemente la pensa diversamente. Scherzi
a parte (ma non troppo: si legga il gustoso saggio di Tom De Marco Why
Does Software Cost So Much [Dorset House 1995]), esistono e quali sono
i veri problemi nella produzione del software?
Frederick Brooks,
l’architetto di sistema di OS/360, ha affermato in un articolo memorabile
[Brooks87] che il software differisce per aspetti essenziali (ineliminabili)
da altri prodotti manifatturieri. Uno degli aspetti essenziali è
la complessità; i programmi software sono quanto di più complesso
sia mai stato costruito dall’uomo. Neanche le CPU sono così complesse.
Per questo ci sono programmi che progettano CPU, ma non programmi che progettano
altri programmi (di dimensioni realistiche). Un altro aspetto essenziale
è che il software è sottoposto in continuazione a cambiamenti
dopo che è già stato consegnato, anzi più un software
ha successo più sarà sottoposto a cambiamenti; pensate a
quanto diversa è la situazione nella produzione di autovetture,
dove una vettura di successo come la vecchia 500 o il Maggiolino può
restare in produzione senza troppe variazioni per decine di anni.
La vita del
software è molto più simile alla vita di un edificio o di
una città, solo con ritmi più rapidi, che alla vita di un’automobile:
anche una città o un edificio hanno una vita e, a meno che li si
imbalsami in un monumento, saranno diversi quando termineranno da come
erano al momento della loro costruzione.
Non è
un caso se le idee dei pattern sono nate per prime nella mente di un architetto.
Prendiamo un libro di Object Oriented Design pre-pattern (Booch, Jacobson,
Coad, Rumbaugh, fate voi): troveremo scritto che scopo del design è
"trasformare i concetti dell’analisi in modo che siano implementabili".
Se l’analisi è ad oggetti e il linguaggio di programmazione è
ad oggetti, non c’è poi molto da trasformare: avanzano sì
e no le relazioni, che più o meno tutti implementano con puntatori.
Tutto lì? Ah no: fa parte del design anche occuparsi della persistenza,
del multitasking (se c’è) e del disegno dell’interfaccia utente.
Non c’è
da stupirsi se, con queste premesse, le discussioni sul design si ingolfano
sul formalismo dei diagrammi di classi, o se qualcuno a buon diritto non
presenta un CASE tool che deriva automaticamente il codice dai diagrammi
dell’analisi, in barba a qualunque sforzo di Design, salvo l’interfaccia
utente che si disegna con pochi colpi di mouse. Devo ancora trovare un
CASE tool che sia stato costruito lui stesso in questo modo. Il riuso?
Sì, di quello qualcuno fra i metodologi ne parla, per dire che è
una cosa difficile da ottenere, che ci riesce solo gente molto brava e
impiegando tanto tempo in più che per fare codice non riusabile:
ma che cosa mai facciano i guru del riuso in tutto questo tempo in più
non si sa bene, i guru delle metodologie non lo dicono.
Un merito del
movimento dei Pattern è di avere richiamato all’attenzione che i
problemi veri di Design non sono come implementare le associazioni o la
persistenza in C++, o se rappresentare le classi usando scatole, nuvolette
o esagoni, ma come scrivere codice facile da estendere e contrarre. La
domanda cruciale del design è quindi: "che cosa cambia se questo
cambia?". I pattern di design sono risposte a questa domanda. Costruire
un sistema software attorno alle risposte a questa domanda ha due benefici:
manutenzione più facile e componenti più facili da riusare.
Questa non è un’invenzione dei Pattern, anzi ne parlava già
Parnas più di 15 anni fa [Parnas79], ma fino a tempi recenti i principi
sull’information hiding di Parnas erano rimasti pressoché lettera
morta [McConnell96]: quegli stessi principi, rivisitati a oggetti e concretizzati,
sono ora espressi come pattern. Per esempio, [GoF] espone diversi modi
di disaccoppiare la creazione di un oggetto dal suo uso (pattern creazionali),
oppure diversi modi di disaccoppiare l’interfaccia di un oggetto dalla
sua implementazione (pattern di comportamento), o ancora modi di disaccoppiare
il comportamento di un oggetto dalla sua struttura (pattern strutturali).
I pattern sono riuso visto dalla parte dell’implementatore.
Il riuso di
cui parlano i pattern non è l’assemblaggio di parti software prefabbricate,
ma l’adattamento intelligente di una parte al tutto.
"Ciascun pattern
descrive un problema ricorrente e la sua soluzione, in un modo tale che
potrà riusare questa soluzione un milione di volte, senza mai farlo
allo stesso modo due volte" dice Alexander.
I pattern rispondono
anche al problema della manutenzione del software, che assorbe in media
la maggior parte del costo globale del software, cioè di adattare
un’implementazione al cambiamento delle specifiche e al conseguente cambiamento
delle interfacce (riuso dell’implementazione).
Distinguere
l’interfaccia dall’implementazione per cambiare l’implementazione mantenendo
fissa l’interfaccia (riuso dell’interfaccia), o costruire il software tramite
composizione di mattoncini modulari come nel Lego, senza mai doverci mettere
le mani dentro, in sostanza disinteressarsi dell’implementazione per curare
solo l’interfaccia, non è nello stile dei pattern: come dice Coplien
in WhyPatternsAreDifferent, "quando la tecnologia o la comprensione maturano
al punto che possiamo catturare formalmente un’idea, essa cessa di avere
la qualità generativa, letteraria che distingue i pattern da altri
metodi".
Un pattern è
un modo di trovare una soluzione con caratteristiche di adattabilità
e generatività che una libreria non può dare. Naturalmente
ci sono dei campi, come la costruzione di GUI, in cui i pattern non sono
più necessari e si può ricorrere a un Wizard: ma, come ci
ricorda Dino Esposito [Esposito96], un conto è fare 100 programmi
di 1000 righe, un conto è farne uno da 100,000 righe.
I pattern
e la qualità del software
Abbiamo detto
che il problema principale del software è il riuso. Quale è
la qualità senza nome delle soluzioni a questo problema?
Risponde Gabriel:
"Credo che sia il codice stesso. Molti parlano dei benefici di avere eccellenti
interfacce e del beneficio di separare l’interfaccia dall’implementazione
in modo che l’implementazione possa variare. Ma pochi parlano seriamente
della qualità del codice stesso. Molti teorici lo lasciano nella
categoria delle cose di cui è meglio non discutere, qualcosa che
deve essere nascosto dalla vista così che possa essere cambiato
in privato. Ma Alexander dice che la qualità viene in parti uguali
dall’artista e dal costruttore che con le sue mani plasma la geometria
che dà all’edificio la sua qualità e il suo carattere. E
il costruttore del software non è il programmatore?".
I programmatori
sono visti in molte organizzazioni come il più basso livello di
manovalanza nella "fabbrica software", secondo un modello tayloriano della
fabbrica che è già datato persino nelle fabbriche vere. È
vero che la programmazione incide solo per il 10-15% sul costo di produzione
del software. Ma la produzione è solo il 30% del costo complessivo
del software: il restante 70% è manutenzione, che è principalmente
riprogrammazione del software e dipende in modo determinante dalla leggibilità
del codice già fatto.
Avere finalmente
riconosciuto ed esaltato l’importanza della qualità della codifica
nella qualità del software è ciò che ha reso i pattern
popolari anche nella comunità dei programmatori, generalmente scettici
sui discorsi teorici; Coplien si spinge a consigliare il pattern ArchitectAlsoImplements
nel ciclo di produzione del software. Va da sé che nell’ottica dei
pattern non basta che "il software funzioni", cioè che implementi
correttamente la specifica e sia conformante alle interfacce, perché
sia considerato valido.
Qualsiasi programma
può essere fatto funzionare da chi l’ha scritto, sappia il suo mestiere
e abbia una buona provvista di caffè. Anche i programmi WORN (Write
Once, Read Never), cioè gli usa-e-getta funzionano, ci vuole pochissimo
a scriverli e magari funzionano pure così bene che non li gettiamo
più via. In certi contesti va benissimo così. Io ne ho scritti
tanti. Il problema è cosa succederà se lo stesso programma
dovrà essere modificato da qualcun altro, o magari dallo stesso
che l’ha scritto ma a distanza di tempo. Io ho tanti nemici fra quelli
cui è capitato di mantenere i miei WORN. Nota per chi non avesse
mai sentito l’acronimo WORN, arcaismo dei tempi dei linguaggi di shell
Unix: sostituite WORN con RAD, o con Wizard-generated, e rileggete.
Vedrete che
non cambia niente.
Framework
L’approccio tradizionale
all’architettura del software è di vedere il tutto come la somma
della parti (componentware): il software come una specie di Lego.
Ma "è impossibile formare qualcosa che abbia il carattere della
natura sommando parti preformate. Quando le parti sono modulari e sono
fatte prima del tutto, per definizione sono identiche, ed è impossibile
per ogni parte essere unica e in accordo alla sua posizione con il tutto"
[Alex79, pag. 368].
Il primo approccio
al riuso del software sono state le librerie, librerie di funzioni o librerie
di classi o di "componenti". Come nota Booch, "le librerie sono una
cosa strana: tutti vogliono aggiungerci qualcosa e nessuno ne vuole prendere
niente". Quel che è peggio, più una libreria è
orientata al riuso più chiede di buttare via tutto il resto perché
non è abbastanza orientato al riuso. Più fornisce componenti
riusabili più è incompatibile con le componenti riusabili
di altre librerie. Certe volte, le librerie di componenti riusabili hanno
persino la biasimevole proprietà di tendere ad alzare il costo del
software e a diminuire la qualità complessiva.
Perché?
Se un modulo costa M lire e viene riusato N volte, si risparmiano M(N-1)
lire.
Va bene? Niente
affatto: primo, il risparmio è direttamente proporzionale al costo
iniziale, per cui i moduli riusabili tendono a diventare spaventosamente
grossi e cari per aumentare il risparmio; spesso, un modulo per essere
riusabile in N posti diversi diventa oltremodo "flessibile" (cioè
grosso e lento), quindi caro, il che contribuisce ad aumentare il risparmio
complessivo.
Secondo, era
veramente necessario riusare N volte quel modulo? Ciò ha veramente
contribuito a migliorare il sistema software complessivo? Fantasie? Conosco
fior di progetti dal costo astronomico e con prestazioni deludenti perché
qualcuno aveva deciso di risparmiare riusando dappertutto un certo modulo
dal costo spropositato (più il costo è astronomico più
si cerca di riusarlo per aumentare il risparmio!) e, tutto sommato, inappropriato
in ognuno dei posti in cui era stato inserito.
I framework cercano
di superare i limiti dell’approccio a librerie componenti ponendo l’accento
sul tutto (il framework) piuttosto che sulle parti.
Un framework
è un insieme di classi che collaborano per fornire servizi al cliente
di un’applicazione; il meccanismo di invocazione fra framework e classi
è ribaltato rispetto a una normale libreria di classi: è
il framework che chiama l’applicazione attraverso qualche meccanismo di
callback, non viceversa. Un framework è prima di tutto un’architettura
applicativa piuttosto che un’architettura di componenti. Il principio ispiratore
del progetto di un framework è (o dovrebbe essere) il "Principio
di Hollywood" ("don’t call us, we’ll call you"). Il framework provvede
il "main", l’applicazione i "dettagli" dei metodi. Si potrebbe parafrasare
un framework dicendo "non chiederti cosa fa il framework per te: chiediti
cosa devi fare tu per il framework". In pratica le cose non sono mai così
semplici perché qualunque framework include anche qualche routine
di libreria, il che fa sentire qualcuno autorizzato a chiamare framework
qualsiasi pacchetto software ci sia da vendere.
I framework
possono usare i pattern per disaccoppiare l’applicazione dal framework,
ma non sono essi stessi pattern né linguaggi di pattern. Un framework
è una specifica realizzazione di uno o più pattern in un
contesto ben delimitato. Un pattern è una cosa logica; un framework
è una cosa fisica. Un pattern è riusabile perché lo
si può applicare a problemi diversi per generare ogni volta la soluzione.
Un framework è riusabile perché incorpora tecniche specifiche
(a loro volta pattern) per rendersi flessibile e disaccoppiato dall’applicazione.
Attualmente molti
progetti cercano di costruire framework in nome del riuso. I framework
e le componenti che ne derivano sembrano attraenti dall’esterno, e rispecchiano
le note proprietà di astrazione e la separazione di interfaccia
da implementazione. Ma molti di questi framework risultano essere al 90%
librerie di soluzioni specifiche, con solo un 10% di parti comuni. "Il
diavolo sta in quel 10%" dice [Cope97]: proprio la struttura interna spesso
si rivela il punto debole e deve essere rifatta per ottenere flessibilità,
efficienza o affidabilità. In questi casi il sedicente "framework"
ha piuttosto l’aspetto di un "patchwork" e non ispira affatto la "qualità
senza nome".
La struttura
di un buon framework deve catturare la struttura del problema; esemplari
sono i pattern di sistema studiati da [Buschmann96].
Un problema non
trascurabile dei pattern è che il principio di Hollywood è
tanto bello e tanto in ma ha un piccolo inconveniente: per documentare
un framework non serve a niente dire cosa fa il framework per l’applicazione,
bisogna dire che cosa il framework si aspetta che l’applicazione faccia
per lui. Documentare in modo tradizionale una cosa di questo genere è
praticamente impossibile.
Basti pensare
che tutti i metodi di Object Oriented Analisys e Design suppongono che
l’applicazione possa liberamente definire il main del flusso di controllo
e le radici delle gerarchie di classi; sono palesemente inadeguati a descrivere
un’applicazione fatta solo di callback e di classi derivate.
La soluzione
"a pattern" consiste in primo luogo nel rendere espliciti i pattern su
cui si basa il framework, in secondo luogo nel descrivere come scrivere
l’applicazione che fa uso di un framework piuttosto che descrivere il framework
stesso. Infatti la parte più importante della documentazione di
una libreria è il Reference Manual, mentre la parte più importante
della documentazione di un framework è lo User Manual. I pattern
sono particolarmente efficaci nel descrivere i framework [Johnson92], perché
evidenziano come usare il software anziché il software stesso. Descrivere
il software serve a implementarlo, non a usarlo. Descrivere come usare
un framework è descrivere come costruire l’applicazione (che non
c’è ancora) in modo che si possa inserire nel framework.
Prendiamo ad
esempio il JDK di Java: la documentazione delle routine che ci sono nel
JDK, per quanto sia eccellente e ben presentata, è meno importante
della documentazione sulle routine che il JDK si aspetta che siano fornite
dall’applicazione (le interfacce astratte), e questo a sua volta è
meno importante che capire come avverrà l’interazione fra delle
routine dell’applicazione fra di loro e quelle di AWT. Sapere che la gestione
degli stream in Java avviene secondo il pattern Chain Of Responsibility,
o che l’AWT nel JDK 1.1 usa il pattern dell’Observer è molto più
importante per il progettista che ricordarsi a memoria le interfacce.
L’approccio
formale ai pattern
L’approccio di
Alexander e di Coplien, che sottolinea l’aspetto generativo dei pattern
e l’organizzazione dei pattern in linguaggi, non è l’unico. Lo stesso
Vlissides, che fa parte della Gang of Four e che condivide con Coplien
l’incarico di chairman dei PLoP, si è occupato attivamente di formalizzazione
e generazione automatica di codice a partire dai pattern [Budinsky96].
Coplien ha replicato sul C++ Report [Cope96] che un pattern destinato a
generare codice non è più un pattern, e che comunque la scrittura
del codice non impegna più del 5-10% del costo di sviluppo di un
software. Però ha sottolineato che un pattern di oggi può
diventare il codice generato automaticamente di domani. Personalmente ho
visto il lavoro di Vlissides e non mi sembra che la sua notazione sia un
passo avanti nella leggibilità del Design, anche se posso ammettere
che un tool lo trovi molto significativo. Come notato da Doug Lea [Lea95],
è una buona idea sviluppare formalismi per catturare meglio la descrizione
di microarchitetture e idiomi ed è anche una buona idea costruire
tool per facilitare l’uso dei pattern. Ma rappresentare un progetto con
qualche formalismo o tool non è lo stesso che scrivere un pattern.
Un pattern deve essere utile alla gente: deve descrivere quando è
applicabile, che problemi risolve e quanto bene li risolve, quali altri
genera, come si correla ad altri pattern. Le categorie di pattern private
del contesto sono tanto utili quanto elencare i 17 modi diversi di implementare
un iteratore: meglio di niente, certo, ma poco utilizzabile. Un pattern
deve stimolare la riflessione e aiutare a migliorare la consapevolezza
del problema che il software è chiamato a rispondere. Né
una notazione formale, né tool di catalogazione o di generazione
automatica possono dare questi aiuti. La dualità fra chi sottolinea
quanto i pattern e i linguaggi di pattern siano generativi, e chi sottolinea
quanto i pattern e i cataloghi di pattern siano riusabili, è vecchia
come il movimento dei pattern. Il primo articolo di Coplien sui pattern
aveva il sottotitolo "a generative pattern"; il primo articolo di Kent
Beck affermava "patterns are a way of developing and packaging reusable
software". La comunità dei pattern si distingue per tolleranza e
pluralità di opinioni.
Non per niente
KindsOfPatterns è un (meta)pattern sul Wiki Wiki WEB (c2.com): ogni
modo di intendere i pattern non è altro che una fra le soluzioni
possibili a un problema in un contesto.
Altri pattern
Una categorizzazione
utile, anche se un po’ forzata, è basata sulla granularità
dei pattern: ci sono pattern di codifica, di design, di analisi e di architettura.
I pattern di design sono storicamente quelli più sviluppati e famosi:
il [GoF] è il capostipite indiscusso dei pattern di design. I pattern
di design studiano le relazioni fra i componenti elementari dell’OOD: oggetti,
interfacce, implementazioni. Alcuni tipici pattern di design sono Proxy,
Visitor, Iterator e Observer. In generale i pattern di design sono
indipendenti sia dalla semantica dell’applicazione che dal linguaggio di
programmazione, anche se il linguaggio condiziona la forma della soluzione,
perché ogni linguaggio ha i suoi pattern e i suoi idiomi.
I pattern di
codifica sono pattern specifici di un linguaggio di programmazione. Essi
insegnano come usare gli elementi del linguaggio per risolvere un problema
di implementazione.
I pattern di
design si collegano in modo naturale con i pattern di codifica: in C++,
il Proxy si implementa bene usando gli smart pointer, il Visitor con il
polimorfismo; il pattern di codifica del counted pointer implementa alcuni
tipi di pattern di Life Cycle. Coplien con [Cope94b], Scott Meyers e Andrew
Koenig sono i punti di riferimento per i pattern di codifica in C++.
I pattern di
codifica non sono la stessa cosa che gli idiomi. Un pattern di codifica
dice quando usare un idioma, non solo come funziona; un idioma è
orientato alla soluzione, un pattern di codifica al problema. Parte della
confusione nasce dal fatto che il punto di riferimento per i pattern di
codifica in C++ è il libro di Coplien intitolato "Advanced C++ Idioms",
perché era stato pubblicato quando i pattern non erano ancora cultura
comune. È comunque evidente che i pattern di codifica parlano degli
stessi pezzi di codice che siamo abituati a conoscere come idiomi di programmazione:
cambia solo la prospettiva.
I pattern di
analisi [Coad95] insegnano a usare la notazione dell’OOA per modellare
un dominio applicativo: per esempio, come trovare le relazioni e le collaborazioni
fra gli oggetti. Riconoscere le strutture ricorrenti di oggetti è
più importante in fase di analisi che non trovare gli oggetti stessi,
con i loro attributi e relazioni.
Un esempio è:
quando compare un insieme di istanze di oggetti della stessa classe, inserire
sempre una classe che li contenga e li gestisca (ContainerAsManager). In
un primo momento magari il contenitore sembra inutile, non si sa quale
semantica assegnargli né quali operazioni abbia, le operazioni "vere"
le hanno gli oggetti contenuti.
Eppure, tutte
le volte che lo ho usato, ho scoperto che più avanti nel design
emergevano delle funzionalità che si applicavano naturalmente al
contenitore, anzi spesso era la stessa presenza di un contenitore a suggerirle.
Questo esempio mostra anche il carattere anticipatorio e generativo dei
pattern. I pattern di architettura [Buschmann96] si occupano dei pattern
che danno forma a un intero sistema software.
Un pattern di
architettura non è riducibile a un’architettura né a un framework,
sebbene per brevità si usi il nome dell’architettura anche per indicare
il pattern corrispondente (p.e. [Buschmann96] parla di pattern Broker,
pattern MVC eccetera): il pattern dice come costruire un’architettura per
risolvere un dato problema in un contesto.
Un framework
e un’architettura sono concetti statici, punti di partenza; un pattern
è dinamico, è generativo.
Ogni pattern
di architettura comprende anche un linguaggio di pattern; per esempio MVC
(Model View Controller) usa fortemente il pattern dell’Observer, così
come il Broker si fonda sul pattern del Proxy: ma un pattern di architettura
non solo un accostamento di pattern di design.
Il tutto è
più della somma delle parti. Il fatto che certe architetture sembrino
proprio un’accozzaglia di pattern e di idiomi affiancati non fa che esaltare
l’importanza dei pattern architetturali.
Conclusione
A che cosa servono
i pattern? Come si usano? Un programmatore deve davvero sapere a memoria
tutti i 23 pattern del [GoF]? E che dire poi dei libri dei convegni sui
pattern, PLoP, dato che ogni anno ne esce uno nuovo? Dobbiamo usare il
registratore sotto il cuscino per impararli tutti?
Evidentemente,
no. Certo è utile conoscere i pattern del [GoF] e anche avere letto
i PLoP non guasta, ma lo scopo dei pattern non è di essere recitati
la sera e la mattina a mo’ di rosario del programmatore. Non funziona così.
Raramente capita di trovare la soluzione a un problema già bella
impacchettata in un pattern.
Più spesso
mi è successo di scoprire una soluzione (a un problema in un contesto),
e solo dopo di accorgermi di un pattern che gli assomigliava. Allora a
cosa mi sono serviti i pattern? Prima di tutto nel metodo di indagine:
problema + contesto = soluzione. Poi, a rinfrancarmi sul fatto che la mia
soluzione "esistesse davvero", per quanto bizzarra mi fosse sembrata, e
che valeva la pena di provarla.
Infine, ma non
ultimo, la razionalizzazione in pattern della mia soluzione che aveva fatto
qualcun altro metteva spesso in luce vantaggi e svantaggi, requisiti e
conseguenze a cui non avevo pensato. I linguaggi di pattern evidenziano
una cosa ancora più importante, cioè quanto due pattern messi
assieme "vadano d’accordo". In fondo, trovare una soluzione a un problema
è facile; il difficile è fare sì che tutte le soluzioni
che mettiamo dentro a un’applicazione convivano felicemente.
La cosa peggiore
che può capitare a un progetto (e a un progettista) è scoprire
di avere messo assieme due pattern incompatibili.
Di solito lo
si scopre un bel po’ di tempo dopo, quando non ci resta che scegliere fra
la ristrutturazione totale del sistema, con conseguente odio di tutti quelli
cui facciamo rifare del codice che già funziona, o una famigerata
patch, per cui saremo odiati da tutti coloro che ci seguiranno.
Mi sono appassionato
ai pattern in seguito a un progetto di cui sono stato responsabile e che,
alla fine, ha pure prodotto una discreta quantità di software, ma
a costo di una serie inenarrabile di ripensamenti e ristrutturazioni. Leggendo
il [GoF] quello che veniva in mente più spesso era "...se l’avessi
saputo prima!".
Spero che almeno
ai pattern risparmino la mia esperienza.
Bibliografia
[Alex79] C. Alexander,
"The Timeless Way of Building", Oxford Press, 1979.
[Brooks87] F.
P. Brooks, "No Silver Bullet", IEEE Computer, April 1987, 10-19.
[Budinsky96]
Budinsky, Finie e Vlissides, "Automatic Code Generation from Design Patterns",
IBM Technical Journal 35(2), 1996; Anche su http://www. almaden.ibm.com/journal/sj/budin/dudinsky.html).
[Buschmann96]
F. Buschmann et al., "A System of Patterns", J.Wiley 1996.
[Cope96] J.
O. Coplien, "Software Patterns", SIGS Management Briefings, 1996.
[Cope97] J.
O. Coplien, "Pattern Languages", C++ Report, Jan. 1997, 15-21.
[Esposito96]
D.Esposito, "Te lo do io il Wizard", Computer Programming N. 53, Dic. 96,
129.
[Garlan95] D.
Garlan et al., "Architectural Mismatch: Why Reuse is So Hard", IEEE Software,
Nov. 1995, 17-26.
[GoF] E. Gamma,
R. Helm, R. Johnson, J. Vlissides, "Design Patterns", Addison Wesley 1995.
[Johnson92]
R. E. Johnson, "Documenting Frameworks Using Patterns", Proc. OOPSLA ’92,
63-76.
[Lea95] http://iamwww.unibe.ch/~fcglib/WWW/OnlineDoku/archive/DesignPatterns/0513.hml.
[McConnell96]
S. McConnell (ed.), "Missing in Action: Information Hiding", IEEE Software,
March 1996, 128-127.
[Parnas79] D.
L. Parnas, "Designing Software for Ease of Extension and Contraction",
IEEE Tr. On Software Eng., SE-5, No. 2, March 1979, 128-137.
[PLoP2] Vlissides
Coplien Kerth (eds.), "Pattern Languages of Program Design 2", Addison
Wesley, 1996.
Graziano Lo
Russo è laureato in Ingegneria Elettronica; è uno specialista
in analisi e sviluppo Object Oriented. Può essere contattato tramite
e-mail all’indirizzo: lorusso@programmers.net
|