Systematic Object Oriented Design

Pubblicato su Computer Programming No. 76

Computer Programming si trasforma e dalle ceneri di Principles&Techniques (rimasta peraltro silente per un paio di mesi) nasce Analisi&Progettazione. Tuttavia il nuovo corso della rivista non prevede rubriche fisse per gli autori, e mi capiterà quindi di apparire in un altro spazio, così come capiterà ad altri di scrivere in questo.

Ciò nonostante, cercherò di mantenere una continuità di contenuti con i tanti punti rimasti "in sospeso" nell’anno passato, a cominciare da un tema cui ho spesso accennato: il Systematic Object Oriented Design (o SysOOD). Alcuni lettori hanno avuto modo di conoscere meglio questo approccio alla progettazione, attraverso articoli apparsi su riviste americane, in occasione dei worklab organizzati da Infomedia Communications, o direttamente attraverso un corso aziendale. Molti altri hanno invece espresso un loro interesse via email, chiedendomi di discutere in modo più approfondito cosa intenda con SysOOD.

L’articolo che state per leggere nasce proprio in risposta a queste richieste. Partendo da alcune considerazioni sull’approccio tradizionale al design, vedremo dapprima le motivazioni di fondo per un cambiamento, i fondamenti di una via più sistematica, e naturalmente un esempio concreto di applicazione delle regole di design e delle tecniche di trasformazione introdotte.

Genesi
Cosa significa progettare un sistema ad oggetti? La progettazione è una fase fondamentale, durante la quale vengono presi in considerazione tutti i requisiti non funzionali del sistema. Così come durante l’analisi ci si concentra su cosa debba fare il sistema, durante il design ci si concentra su come debba farlo, considerando gli obiettivi di riusabilità, estendibilità, testabilità, manutenibilità, eccetera che costituiscono le grandi promesse della tecnologia ad oggetti.

Come fa un buon progettista a raggiungere tutti gli obiettivi di cui sopra? Nonostante le mirabilie promesse dai vari linguaggi e (soprattutto) dai produttori di tool, non è affatto sufficiente "adottare gli oggetti" per ottenere i risultati sperati. Ad esempio, passare direttamente da un modello di analisi ad oggetti ad una implementazione, sempre ad oggetti, difficilmente porterà ad un sistema semplice da estendere: l’analisi modella il problema, e senza opportuni accorgimenti (introdotti proprio in fase di design) i naturali cambiamenti nel problema da risolvere potrebbero avere un impatto notevole sulla struttura complessiva della soluzione. Occorre quindi uno sforzo ulteriore, deliberatamente orientato ad ottenere benefici a medio-lungo termine; questo è peraltro uno dei nodi centrali che vanno ben compresi anche dal management: i vantaggi della tecnologia ad oggetti si notano principalmente sulla distanza, e non nella rapidità di consegna [Pes96].

Un buon progettista ha probabilmente imparato l’arte del design leggendo, studiando, osservando il lavoro altrui, pagando sulla propria pelle il prezzo di scelte poco oculate, e conservando invece la struttura delle soluzioni vincenti. Un percorso difficile, non particolarmente efficiente, e che non garantisce neppure una futura ripetibilità dei successi ottenuti.

D’altra parte, ciò è in larga misura una conseguenza dell’approccio tradizionale all’insegnamento del design. Chi prova a leggere i testi sacri della progettazione ad oggetti (ad esempio quelli che accompagnano le grandi metodologie: OMT, Booch, ecc) si accorge rapidamente che l'enfasi viene posta in massima parte sul processo di sviluppo (produzione di diagrammi delle classi per rappresentare la struttura statica, di scenari di interazione e diagrammi di stato per modellare gli aspetti dinamici, definizione di pre-post condizioni, ecc), seguita poi dalle consuete ramanzine sui buoni princìpi di progettazione ("gli oggetti devono essere autonomi", "le astrazioni non devono dipendere dai dettagli", ecc).

Ben poco viene detto su come ottenere tali risultati. Nel migliore dei casi vengono presentati alcuni esempi, lasciando a chi legge il difficile compito di astrarre le tecniche utilizzate e di applicarle all'interno dei propri progetti. Ancora peggio, spesso gli esempi si riferiscono a classi di infrastruttura (liste, alberi, semplici componenti di interfaccia), non ai più complessi scenari applicativi in cui molte classi cooperano per ottenere un risultato, e dove pur colloquiando devono (ad esempio) essere mantenute indipendenti per promuoverne il riuso. Dopodiché, ci si ritrova a lavorare in progetti reali, ovvero a dover prendere una serie di decisioni complesse, in tempi brevi e purtroppo senza un vero metodo di lavoro. Sfortunatamente, è sin troppo frequente che un insieme di piccole variazioni dalla "struttura ideale" (un esempio: collocare una responsabilità/funzione nella classe sbagliata) portino ad ottenere comunque un sistema "che funziona", ma che non mostra le desiderabili caratteristiche di cui sopra.

Non a caso, negli ultimi anni si è andato affermando l'uso dei design pattern: soluzioni sperimentate a problemi ricorrenti di design. Mentre gran parte delle metodologie sono guidate dai princìpi di progettazione, i pattern nascono dalla pratica e sono effettivamente molto utili per evitare di reinventare la ruota. Un pattern "funziona" soprattutto perché lo ha dimostrato sul campo, non necessariamente perché incarna un principio astratto di design.

L'uso dei pattern, tuttavia, oltre ad essere limitato all'impiego di soluzioni già sperimentate, richiede anche una visione molto acuta, che nuovamente si ottiene solo con l'esperienza. I pattern di design propongono soluzioni a macro-problemi che vanno identificati in anticipo: è piuttosto difficile partire con un design semplice e migliorarlo gradualmente attraverso l'uso dei pattern. Inoltre, proprio perché affrontano macro-problemi, molti pattern sono difficili da capire "a fondo": spesso pattern diversi sembrano essere varianti di un unico concetto, e non è affatto semplice scegliere "il pattern giusto". Rimangono comunque alcuni innegabili vantaggi dell’approccio a pattern, ad esempio la funzione di "vocabolario esteso" [Bec96] che allarga la banda di comunicazione tra sviluppatori.

Riassumendo, il problema maggiore dell'approccio "a princìpi" è che di norma non fornisce alcuna guida concreta verso un design che rispetti i princìpi stessi. Una delle ragioni è la pretesa di catturare sotto un unico principio un numero enorme di casi: purtroppo, quando si sale troppo nella scala dell'astrazione, si rischia di perdere la necessaria concretezza. I princìpi ci dicono cosa dobbiamo mirare ad ottenere, ma non come ottenerlo [Pes97a]. Inoltre, per ottenere la sufficiente astrazione i grandi princìpi utilizzano termini volutamente informali ("astrazione", "dettaglio", "aperto", ecc) che non consentono neppure di decidere in modo non-ambiguo se il nostro design rispetta o meno i princìpi stessi.

Per contro, il problema principale dell'approccio "a pattern" è che le soluzioni proposte spesso sono di livello molto alto, troppo strutturate, mentre in pratica ci troviamo spesso a combattere con problemi localizzati. Anzi, in molti casi è necessario trovare soluzioni localizzate per evitare che le modifiche abbiano un impatto globale sul progetto. Ogni pattern è ottenuto cercando di bilanciare forze diverse, che hanno più o meno rilevanza nei vari contesti applicativi; tuttavia adattarne la struttura alle sole forze in gioco nei nostri casi concreti è un compito non banale, e di nuovo questo compito spesso non è agevolato dal pattern stesso. In generale, i pattern ci mostrano come ottenere un risultato, ma non i singoli passi compiuti per ottenerlo, e quindi non ci mettono facilmente in grado di ri-bilanciare i contributi apportati da ogni forza alla soluzione complessiva.

Tuttavia, entrambi gli approcci hanno moltissimo da insegnare. Semplicemente, credo che i loro insegnamenti possano essere resi più efficaci spostando leggermente il punto di osservazione, e seguendo una strada alternativa che in molti casi si rivela essere un ponte fra i due mondi.

Verso un approccio sistematico
Nel 1995 ho iniziato un percorso alternativo, cercando però di evitare il classico errore di "ripartire da zero" e mirando invece ad estrarre tutto il possibile apporto sia dalle metodologie tradizionali che dai pattern, ed in realtà anche da altre discipline. Fondendo tutto ciò, ovviamente, con l'esperienza acquisita come progettista e system architect in grandi progetti. Questo processo di razionalizzazione si è rivelato piuttosto lungo, ma non privo di risultati. In seguito, oltre a continuare sul lato "di studio", ho iniziato a condividere con altri sviluppatori i risultati ottenuti, e soprattutto ho continuato a metterli alla prova in ogni progetto reale al quale lavoravo. Progetti che erano anche lo stimolo "concreto" del processo di razionalizzazione: di fronte ad ogni scelta di design, che non di rado risolvevo sulla base dell'esperienza in modo quasi automatico, ho cercato di fermarmi, di formalizzare le regole che stavo adottando e di risalire ai princìpi che si nascondevano dietro tali regole. Quando osservavo risultati e proposte vincenti di altre persone, nel lavoro o nella lettura di pubblicazioni varie, cercavo (e cerco ancora) di identificare qualche criterio di livello più alto, che mi garantisse la ripetibilità di quel successo. Di fronte a scelte più difficili, che magari sfuggivano sino ad uno stato più avanzato, costringendo ad un successivo refactoring, cercavo di fermarmi ad identificare casistiche più ampie di applicabilità, cercando di scoprire quali forze avevano reso fragile la struttura iniziale, quali rendevano robusta quella migliorata, e quali tecniche sistematiche si potevano adottare per passare dall’una all’altra.

L'approccio alla progettazione che ne è derivato (che ho battezzato Systematic Object Oriented Design) è decisamente diverso dai precedenti, almeno per quanto riguarda la progettazione ad oggetti: si avvicina maggiormente alle tecniche di progettazione utilizzate in ambienti più consolidati, ad esempio le tecniche di normalizzazione dei database relazionali. Pur cercando di trarre il massimo beneficio dagli approcci tradizionali, il Systematic OOD non si concentra né sui grandi princìpi né sulle macro soluzioni, ma su tecniche basilari di trasformazione. Proprio attraverso queste tecniche concrete possiamo arrivare a rispettare i grandi princìpi, o a ricostruire le macro soluzioni vincenti dei pattern.

Il concetto di fondo è molto semplice: ottenere un design "banale", che non rispetta nessun principio di buona progettazione, è decisamente semplice. Molti di noi possono anche saltare questa fase e passare direttamente a scrivere del codice. Naturalmente, in molti casi i risultati non brilleranno per riusabilità, manutenibilità, estendibilità e così via. Tuttavia, fissata una struttura di massima, gli ingredienti mancanti sono spesso una serie di accorgimenti, di scelte localizzate, di interventi mirati che modificano il design originale e lo riportano entro schemi più eleganti e flessibili. Come altri autori hanno fatto osservare, "...the essence of design is transformation. [...] But these transformations are by no means trivial" [CLF92].

Queste trasformazioni vengono applicate "al volo" dai progettisti esperti, facendo sembrare la progettazione un'arte difficile da imparare. Viceversa, quando applichiamo un pattern, spesso introduciamo nel nostro progetto una macro-struttura ottenuta tramite una combinazione di trasformazioni. Capire i singoli passi che portano da una struttura banale a quella "ripulita" dei pattern permette di conseguire al meglio il reale obiettivo dei pattern stessi: di essere adattati al contesto in cui vengono applicati, e non copiati come ricetta di cucina.

Quello che ho cercato di fare negli ultimi anni, e che continuo a fare tuttora, è codificare il maggior numero possibile di queste tecniche di trasformazione. In molti casi, si tratta di tecniche estremamente semplici, una volta che sono state presentate; scoprirle e classificarle, però, richiede uno sforzo sorprendentemente grande. Non di rado ci si rende conto che una tecnica ritenuta "fondamentale" altro non è che la composizione di tecniche più elementari. Ed ovviamente, occorre prestare attenzione a non cadere in trasformazioni banali, non sufficientemente ricche dal punto di vista semantico: in fondo, è stato già dimostrato [Ber91] che esiste un substrato minimo di trasformazioni banali a livello di OOP (es. spostare un data member) su cui possiamo costruire ogni trasformazione complessa. Tali micro-trasformazioni possono essere utili per verifiche di correttezza, ma sono troppo poco espressive per essere utili in fase di design (formano invece un utile substrato per una teoria algebrica delle trasformazioni, che comunque è al momento ai margini dei miei interessi).

Fortunatamente, lo sforzo di individuare regole e tecniche associate è soltanto a carico di chi vuole estendere le basi del metodo, non di chi lo utilizza nel lavoro di ogni giorno. Mentre i metodi basati sui grandi princìpi lasciano spesso a carico dei progettisti lo sforzo di imparare a rispettarli, il mio obiettivo è di spostare le difficoltà su chi propone nuove tecniche, non su chi deve applicarle.

SysOOD
In una visione più organica, il Systematic OOD si basa su diversi elementi:
  • Un insieme di condizioni, verificabili in modo sistematico ed oggettivo, che possono portare a problemi di vario tipo (impossibilità di riusare una classe, o di supportare nuove funzionalità, o di estendere una gerarchia, eccetera). Ogni condizione viene normalmente fatta risalire alla violazione uno o più princìpi di design, ma non pretende di catturare una casistica troppo ampia: si preoccupa invece di essere verificabile e costruttiva. Il progettista verifica l'insorgere di queste condizioni durante la normale attività di progettazione. Inoltre, ogni regola spiega chiaramente quali sono le conseguenze di una sua violazione: il progettista può benissimo decidere che tali conseguenze sono perfettamente ragionevoli ed accettabili nel suo caso concreto. L’importante, come sempre, è che questo tipo di decisioni siano prese consciamente in fase di design, e non succeda invece di scoprire una conseguenza inattesa molto più avanti, ad esempio in fase di manutenzione.
  • Un insieme di tecniche di trasformazione. Per ogni condizione problematica, esistono diverse tecniche di trasformazione che possono essere applicate, in modo sistematico, per eliminare il problema e riportare il progetto entro il rispetto dei migliori princìpi di design. La scelta della migliore tecnica dipende ovviamente dai molti fattori al contorno, e richiede quindi una valutazione del progettista che mantiene un ruolo molto importante: il Systematic OOD non ha come obiettivo il deskilling del progettista. Ogni tecnica è preferibilmente accompagnata da alcune regole euristiche che aiutano a scegliere la trasformazione migliore.
  • Un insieme di criteri per valutare le qualità relative di design alternativi. Spesso ci troviamo di fronte a delle scelte di progetto e non abbiamo basi concrete sulle quali decidere. Esistono però dei criteri fondamentali, come stratificazione, coesione ed accoppiamento, che possono essere adattati anche al paradigma ad oggetti e possono fornire una guida oggettiva in molte situazioni (vedere [Pes97b] per alcuni esempi reali).

È importante notare che il SysOOD, come descritto sopra, non si pone in competizione o in contrasto con alcuna metodologia esistente. Non richiede di apprendere una nuova notazione, o di abbandonare metodi e tool sinora utilizzati. Sia che usiate Booch, OMT, UML, un approccio informale al design, eccetera, potete applicare le tecniche del SysOOD; se esiste un condizionamento nelle regole e tecniche che ho sinora identificato, è quello di essere pensate per linguaggi con controllo statico dei tipi (C++, Java, Delphi, Ada95, Eiffel, ecc). Io ho avuto modo di utilizzarle in contesti molto diversi, in progetti di media e grande dimensione, senza nessun problema di integrazione con i diversi processi di sviluppo. In fondo, l'obiettivo finale è di colmare le lacune lasciate dagli approcci tradizionali, non di sostituirsi a loro. Nello stesso senso, nulla impedisce di usare i pattern insieme alle regole di trasformazione del Systematic OOD. Anzi, usare un pattern diventa decisamente più semplice una volta comprese le regole di trasformazione che lo generano. I più noti pattern di design (ad esempio l'Observer, o Document/View che dir si voglia) sono infatti tutti ottenibili partendo da un design "banale" ed applicando le regole di trasformazione proprie del Systematic OOD. Ma, ovviamente, mantengono la loro importanza individuale e possono essere usati in un unico passo da chi voglia utilizzarli come elementi di design.

Un esempio di regola
Uno dei princìpi di design più utili, omnicomprensivi, ma anche soggetti ai problemi citati poco sopra è il seguente: "le astrazioni non devono dipendere dai dettagli". Questo principio è stato più volte riscoperto in letteratura, ed una delle sue formulazioni più note è conosciuta come "the Dependency Inversion Principle" [Mar96]. Le ragioni di fondo che sostengono il principio sono relativamente semplici: una astrazione è potenzialmente riusabile in molti contesti ed applicazioni; se la leghiamo a dettagli, per natura specifici di un contesto/applicazione, ne riduciamo la riusabilità. Inoltre, introduciamo possibili vincoli di manutenzione tra i dettagli (spesso più instabili delle astrazioni) e l’astrazione stessa.

Se la fondatezza del principio è innegabile, altrettanto non si può dire della sua precisione. Cosa voglia dire "astrazione" o "dettaglio" è del tutto soggettivo [Par79]. Non è possibile prendere un diagramma e, senza una conoscenza della semantica implicita nei nomi delle classi, decidere se rispetta il principio o meno. Questo vanifica ad esempio ogni possibilità di controllo automatico, contribuendo a relegare i CASE tool a strumenti di diagrammazione glorificati dalla generazione di codice. Peraltro, una volta osservata una violazione, subentra un secondo problema: il principio è di per sé non-costruttivo, ovvero non suggerisce come rimediare ad una violazione.

In casi simili, possiamo accettare i problemi come inevitabili, oppure possiamo cercare strade alternative. Ad esempio, potremmo cercare di identificare dei sotto-casi (condizioni necessarie ma non sufficienti) che coprano comunque una problematica rilevante, e che siano per contro verificabili in modo oggettivo, senza elementi informali come "astrazione" o "dettaglio". Ad esempio, una volta introdotta una nozione di "dipendenza fra classi" (A dipende da B se e solo se A utilizza B nella sua interfaccia od implementazione) possiamo identificare la seguente condizione necessaria affinché valga il principio di cui sopra: il grafo delle dipendenze tra classi deve essere aciclico. In quanto aciclico, può essere partizionato in strati, in modo che ogni classe appartenente ad uno strato dipenda solo da classi appartenenti a strati superiori (design stratificato).

Questa condizione non è sufficiente a garantire il principio di cui sopra: semplicemente, copre una casistica piuttosto frequente; altre regole di design possono coprire altri casi di violazione del principio informale non identificate della regola formale data sopra.

Una volta che riscontriamo una dipendenza circolare, quali sono le conseguenze? La prima e più immediata conseguenza è che l’unità di riuso diventa il ciclo, e non la classe: per riusare una classe coinvolta nel ciclo occorre riusare anche le classi dipendenti, ovvero l’intero ciclo. Una conseguenza collaterale è che spesso le modifiche ad una delle classi coinvolte nel ciclo si ripercuotono anche su altre classi collegate.

Se queste conseguenze sono inaccettabili nel nostro caso concreto, dobbiamo potervi rimediare. Qui entrano in gioco le tecniche di trasformazione, e per il caso particolare che stiamo considerando ho sinora identificato 5 tecniche base ed una derivata. Una delle più semplici, che molti di voi avranno sicuramente usato in modo intuitivo, è rappresentata in figura 1 per il caso di due classi coinvolte. Il nome Asymmetric Splitting suggerisce l’asimmetria della soluzione, che spesso sfrutta proprio una asimmetria di cardinalità già presente nel diagramma iniziale. Altre tecniche di trasformazione sfruttano invece una possibile simmetria, o altre particolarità del grafo di dipendenza (ad esempio, quando una delle dipendenze è data dall’ereditarietà). Applicando l’Asymmetric Splitting, otteniamo riusabilità dal lato della classe B ed estendibilità dal lato della classe A.

Un esempio concreto di applicazione
Ho scelto di utilizzare l’esempio che segue per tre ragioni. La prima è che si tratta di un caso reale ma abbastanza semplice da essere discusso in poco spazio. La seconda è che dimostra come un pattern molto conosciuto si possa facilmente derivare da un design banale, applicando le regole e le tecniche del SysOOD. La terza è che (in linea con i princìpi dell’OOP :-) posso riusare i diagrammi apparsi in un altro articolo [Pes98] che si dedica più in dettaglio alla derivazione dei pattern.

Consideriamo la figura 2; qui un progettista non molto esperto ha modellato una applicazione che gestisce due tipi di documento, su ognuno dei quali esistono due tipi di viste. Le viste concrete accedono al documento concreto per leggerne e modificarne i dati. I documenti concreti parlano con le viste concrete per mantenerle sincronizzate dopo ogni modifica. Ne consegue un’ovvia dipendenza circolare. Analogamente, il document manager crea i documenti, e ogni documento notifica al manager la propria distruzione. Di nuovo, abbiamo una dipendenza circolare.



Nessuna di queste dipendenze circolari è accettabile: noi vogliamo poter riusare il document manager con altri documenti, ed i documenti con altre viste. Vogliamo anche una estendibilità sul versante documenti e viste. Questo suggerisce (anche avendo a disposizione la lista completa di trasformazioni) di scegliere l’Asymmetric Splitting. Iniziamo ad applicarlo alle viste concrete: otteniamo il risultato di figura 3, dove la classe View (corrispondente ad A* in figura 1) disaccoppia i documenti concreti dalle viste concrete. In figura 3 ho anche fuso le varie classi A* in una sola classe View: ovviamente, avrei potuto fare tutti i singoli passi e fondere le classi interfaccia in una fase separata. Anche la fusione è una tecnica di trasformazione, per quanto elementare.



Ora possiamo applicare la stessa trasformazione tra Document e Document Manager. Il risultato (da cui ho eliminato Document2 per semplicità) è visibile in figura 4: partendo da un design banale abbiamo ottenuto in modo sistematico (attraverso regole e trasformazioni) la struttura del pattern Observer. Non solo, strada facendo abbiamo anche capito meglio il ruolo di ogni classe: ad esempio, se nella nostra applicazione avessimo un solo tipo di documento (es. Document1), e non prevedessimo un riuso od una estensione di tale classe, potremmo evitare di introdurre la classe astratta Document: questa emerge infatti come elemento di disaccoppiamento tra DocumentManager ed i documenti concreti.

Conclusioni
Per quanto semplice, credo che l’esempio precedente dia una buona idea di quanto si può ottenere con un buon insieme di regole (attualmente ne ho introdotto solo 4, ma l’impatto è comunque notevole) e di tecniche di trasformazione (attualmente sono a quota 18). Le regole indicano i punti deboli, le tecniche di trasformazione indicano concretamente come rimediare. È quindi un metodo di verifica ma anche generativo. Immagino sia anche evidente che la regola in questione, e la tecnica di trasformazione che abbiamo visto, si apprendono molto facilmente e si possono applicare senza alcuna necessità di assimilare un grande apparato formale.

Naturalmente, il SysOOD non pretende di rivoluzionare il design. Anche se ho grande fiducia nelle possibilità dell’approccio, le regole e le tecniche che ho sinora identificato sono ancora poche, anche se coprono un buon numero di problemi ricorrenti (e permettono di derivare la maggior parte dei pattern conosciuti). Ad esempio, conosco un paio di problemi non troppo complessi, a cui so dare una soluzione "ideale" a livello intuitivo, so trovare quale grande principio di design vìolino le soluzioni non ottimali, ma non so ancora trovare una regola formalizzabile che identifichi tali soluzioni come imperfette. Inutile dire che conto prima o poi di trovarla, o che qualcun altro la trovi: in fondo, il metodo è totalmente aperto al contributo di chiunque voglia impegnarsi a rendere più preciso, ed al contempo anche più fruibile dagli altri, il proprio approccio al design.

Per concludere, invito tutti i lettori che vogliano vedere approfondito questo tema (ad esempio, dedicando alcune puntate ad una rassegna delle regole e delle trasformazioni relative) a mandarmi qualche riga via email. Il feedback dei lettori è sempre fondamentale per chi scrive, ed in questo caso particolare credo lo sia ancora di più.

Bibliografia
[Bec96] Kent Beck ed altri, "Industrial Experience with Design Patterns", Proceedings of the 18th International Conference on Software Engineering, Germany, March 1996.
[Ber91] Paul L. Bergstein, "Object-Preserving Class Transformations", Proceedings of ACM OOPSLA ’91.
[CLF92] Dennis de Champeaux, Doug Lea, Penelope Faure, "The Process of Object-Oriented Design", Proceedings of ACM OOPSLA ’92.
[Mar96] Robert Martin, "The Dependency Inversion Principle", C++ Report, May 1996.
[Par79] David Lorge Parnas, "Designing Software for Ease of Extension and Contraction", IEEE Transactions on Software Engineering, Vol. 5 No. 2, March 1979.
[Pes96] Carlo Pescio, "Introduzione della Tecnologia ad Oggetti in Azienda: Gestione e Prevenzione dei Rischi", Proceedings della Conferenza "Tecnologia ad Oggetti per l'Industria", dicembre 1996. Disponibile sulla mia home page.
[Pes97a] Carlo Pescio, "Principles Versus Patterns", IEEE Computer, September 1997.
[Pes97b] Carlo Pescio, "Manager Classes: a Software Engineering Perspective", Object Expert, May/June 1997.
[Pes98] Carlo Pescio, "Deriving Patterns from Design Principles", Journal of Object Oriented Programming, November/December 1998.

Biografia
Carlo Pescio (pescio@eptacom.net) svolge attività di consulenza in ambito internazionale nel campo delle tecnologie Object Oriented. Ha svolto la funzione di Software Architect in grandi progetti per importanti aziende europee e statunitensi. È incaricato della valutazione dei progetti dal Direttorato Generale della Comunità Europea come Esperto nei settori di Telematica e Biomedicina. È laureato in Scienze dell'Informazione ed è membro dell'ACM, dell'IEEE e della New York Academy of Sciences.