La presentazione è in caricamento. Aspetta per favore

La presentazione è in caricamento. Aspetta per favore

OOAD - Seconda parte.Design (progettazione) Progettazione ad oggetti Cosa significa progettare un sistema ad oggetti? La progettazione è una fase fondamentale,

Presentazioni simili


Presentazione sul tema: "OOAD - Seconda parte.Design (progettazione) Progettazione ad oggetti Cosa significa progettare un sistema ad oggetti? La progettazione è una fase fondamentale,"— Transcript della presentazione:

1 OOAD - Seconda parte.Design (progettazione) Progettazione ad oggetti 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à, che costituiscono le grandi promesse della tecnologia ad oggetti. Come fa un buon progettista a raggiungere tutti gli obiettivi di cui sopra? Nonostante le promesse dei vari linguaggi e (soprattutto) dei produttori di tool, non è affatto sufficiente "adottare gli oggetti" per ottenere i risultati sperati.

2 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. 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.

3 Obiettivi della fase di Design Trasformare i concetti dell’ analisi di alto livello, usati per descrivere il contesto del problema, in una forma implementabile Specificare i vincoli HW e SW Bilanciare tra concetti di riutilizzo, modificabilità ed efficienza

4 Passi della fase di Design System Design –Architettura tecnica: Configurazione HW e SW adatta all’ ambiente target –Architettura d’ applicazione: suddivisione del sistema in sottosistemi e loro relativa associazione ai processi Object Design –Rifiniture e estensioni del modello di analisi

5 System Design produce : l’ architettura del sistema di alto livello, dove si specificano la configurazione HW e SW e l’ ambiente operativo. l’architettura dell’ applicazione, che implica prendere decisioni riguardo l’ organizzazione del sistema in sottosistemi, il modo di allocazione dei sottosistemi sull’ HW Object Design produce: rifiniture ed estensioni del modello di analisi per permettere un’ implementazione efficiente La definitiva definizione delle classi e delle associazioni usate nell’ implementazione, interfacce, algoritmi e metodi da usare.

6 System Design. Bisogna comprendere che costruire un’ applicazione con un’ appropriata architettura e’ un fattore critico di successo per qualsiasi progetto OO. Bisogna essere in grado di applicare i piu’ importanti criteri per costruire una buona architettura. Scopo di questa sessione e’ rendere consapevoli che lavorare con gli oggetti non e’ sufficiente per costruire applicazioni OO, gli oggetti devono essere sapientemente organizzati in moduli, sottosistemi, nel contesto del Sistema completo. Il risultato di questo processo e’ l’ archirettura dell’ applicazione.

7 Object Design. Essere in grado di trasformare concetti di analisi di altolivello (associazioni, vincoli, ereditarieta’ …) in concetti facilmente implementabili. Essere in grado di applicare ottimizzazioni ad un Object Model inefficiente. Essere in grado di mappare un OM in un DB per risolvere problematiche di persistenza dati Lo scopo di questa sessione e’ discutere in dettaglio i passi che devono essere risolti durante la fase di Object Design e applicare tecniche per risolverli.

8 Qualche regola utile per affrontare la fase di progettazione, ossia ‘come’ realizzare quanto e’ stato specificato nella fase di analisi: Occorre individuare: 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 di 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 estensione e/o di manutenzione.

9 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 principi 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.

10 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, e l’ accoppiamento, che possono essere adattati anche al paradigma ad oggetti e possono fornire una guida oggettiva in molte situazioni.

11 Consideriamo la figura; qui e’ stata modellata 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.

12 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.La figura seguente, dove la classe View disaccoppia i documenti dalle viste.

13 Ora possiamo applicare la stessa trasformazione tra Document e Document Manager. Il risultato è visibile nella figura seguente: si e’ partiti da un design banale e applicato in modo sistematico regole di trasformazione. 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 reali.

14 Architettura Definizione dell'architettura Al termine della fase di analisi, abbiamo identificato un certo numero di classi, relazioni, ed eventi tipici del sistema; ci si potrebbe chiedere se tale struttura non identifichi immediatamente l'architettura del sistema stesso, proprio perche' ne cattura gia’ gli elementi essenziali. Cio‘ sarebbe auspicabile, e di fatto costituirebbe un enorme punto a favore dell'approccio object oriented, ma purtroppo in genere non corrisponde a verita'. Se il sistema e’ relativamente semplice, come nel caso degli esempi necessariamente ridotti che si trovano in libri ed articoli, si ha in effetti una stretta corrispondenza tra il risultato dell‘ analisi e l'architettura del sistema; ma non appena si esce dai confini ridotti della divulgazione, tuttavia, la complessita' dei sistemi richiede metodi piu' completi.

15 Architettura tecnica Definizione della piattaforma (o delle piattaforme) che dovranno ospitare il sistema. Questo include l'architettura hardware, almeno negli aspetti piu' rilevanti, ossia quegli aspetti visibili dagli altri team di progetto. Il lato hardware si estende inoltre a tutte le periferiche che dovranno essere controllate dal sistema, aspetto particolamente importante in software di controllo industriale. La piattaforma comprende inoltre il sistema operativo, gli eventuali gestori di basi dati con i quali e' necessario interfacciarsi, eventuali sistemi GUI per i quali il sistema dovra' essere disponibile.

16 La scelta della piattaforma andrebbe eseguita piuttosto rapidamente; anche se cio' puo' sembrare limitativo, in quanto future scelte architetturali potrebbero suggerire piattaforme alternative, in pratica la scelta non di rado e' abbastanza semplice. Spesso, infatti, la scelta di una piattaforma e' motivata piu' da ragioni economiche o strategiche che non tecniche: talvolta un sistema operativo poco diffuso o sperimentale(privo di licenze gravose) potrebbe forse essere la piattaforma ideale per l’ applicazione, ma se si vuole vendere il prodotto bisogna orientarci verso uno standard di mercato(per problemi di compatibilita’). Oppure, la scelta di una particolare architettura hardware e' motivata da precedenti esperienze interne all’ azienda, o dalla richiesta del committente.

17 Nel caso di un sistema di misura ho gia’ avuto modo di fare cenno alla complessita’ dei componenti coinvolti. Molto genericamente quasi tutte le aziende di questo settore hanno adottato Microsoft come fornitore, Questo significa MSWindows (da ed. 95  XP) come s.o. base per i loro sistemi, OFFICE per i programmi accessori, in particolare Access per i DB. Si e’ ormai standardizzata la comunicazione tra applicazione di misura e controllo (HW e Firmware) via USB, in passato la comunicazione avveniva via RS232. Gli utensili sono invece standard di mercato (quasi tutti), hanno un prorpio protocollo di comunicazione. I controlli, quindi il cassetto elettronico, rimane uno sviluppo proprietario, qualche azienda sta iniziando a produrli in modo standard. Quindi anche i protocolli di comunicazione sono proprietari. Facile capire perche’: in questo modo si osteggia la concorrenza nella guerra al retrofit.

18 Architettura dell’ applicazione La struttura statica. Questa parte sara' in effetti largamente basata sul risultato dell'analisi, ed identifica le componenti statiche del problema, ovvero le astrazioni di piu' alto livello.Il punto di partenza per definire la parte statica dell'architettura sono proprio le "categorie di classi" o "i soggetti", ovvero insiemi di classi omogenei e coesivi, e debolmente accoppiati con il resto del sistema. La struttura dinamica. Questa parte, per la cui definizione sara' fondamentale ma non sufficiente identifica le componenti attive del sistema, il comportamento (behaviour) del sistema stesso, la concorrenza interna, le interazioni con il mondo esterno. La struttura dinamica puo' essere molto semplice in sistemi mono-task, ad esempio un programma di data-entry, ma puo' arrivare a livelli di complessita' enormi in sistemi dinamici e distribuiti. Una pianificazione di consegna. Troppo spesso nella trattazione teorica dello sviluppo si dimenticano le esigenze contingenti, come la realizzazione di un prodotto semi-funzionanti (α release, β release) a scopo dimostrativo, ma che sono veri e propri sotto-sistemi completamente operanti. Tali vincoli sono invece fondamentali per la definizione di una architettura adeguata per il sistema da realizzare: se e' necessario avere una versione intermedia che possa colloquiare con una base dati esistente, partire con una architettura ambiziosa totalmente basata su oggetti persistenti puo' portare a problemi piuttosto difficili da risolvere e ad imbarazzanti ritardi di consegna. La realizzazione di sistemi complessi include un buon processo di pianificazione, che influisce sulle componenti tecniche del progetto: puo' non piacere, ma e' vero.

19 La pianificazione di consegna e' nuovamente un fattore extra-tecnico, anche se un management illuminato dovrebbe sempre tenere in considerazione gli aspetti tecnici di alto livello, durante la stesura dei tempi per il design e l'implementazione. Nuovamente, di rado esiste una grande liberta' di scelta: trascurando le situazioni del tipo "tutto e subito", sintomo caratteristico di un approccio errato alla pianificazione dei progetti, esistono priorita' ben definite che devono essere rispettate da un punto di vista contrattuale. Far combaciare tali esigenze con la definizione architetturale del sistema non sempre e' semplice o possibile (non a caso i ritardi di consegna non stupiscono piu' di tanto nel mondo del software, ma vengono sempre rinfacciati), ed e' realmente necessario che i progettisti interagiscano e cooperino con il project manager (e viceversa) alla stesura di un piano realistico che non imponga scelte architetturali troppo limitative. La definizione della struttura statica e della struttura dinamica costituiscono invece il nucleo tecnico del design architetturale, e come tali sono le piu' interessanti.

20 Design Architetturale Tutti gli elementi fondamentali di un problema, che sono stati evidenziati dalla fase di analisi saranno modellati come classi; allo stesso modo, anche la gestione files e la memoria condivisa sono classi, ed essendo implicito nella descrizione informale che dal punto di vista della rappresentazione la provenienza dei dati e' irrilevante, le caratteristiche comuni di file e memoria condivisa possono essere astratte in una classe base "stream". Inoltre, l'utente viene inserito come parte del dominio del problema: anche se non era esplicitamente nominato nella descrizione informale(ma negli use cases della fase di analisi compare), e' evidente che i "click" ed i "doppi click“, cioe’ tutte le interazioni- utente hanno un'origine. E' buona norma rappresentare nel modello analitico anche gli elementi del mondo esterno che interagiranno con il sistema informatico: cio' consente una migliore vista di insieme, e permette di individuare a colpo d'occhio (con un po' di esperienza) le situazioni in cui l'interazione tra alcuni elementi dell'ambiente ed il sistema e' troppo debole o troppo forte.

21 Le scelte architetturali tendono ad essere pesantemente influenzate dalla piattaforma software sulla quale intendiamo implementare l'applicazione: ad esempio, una piattaforma nella quale esista gia' una astrazione dei file e della memoria condivisa, in termini di memory-mapped-file, porterebbe ad una struttura molto diversa da quella richiesta da una piattaforma con una distinzione netta tra memoria condivisa e file, e dove sia quindi compito del progettista fornire la necessaria astrazione. Per questo e' cosi' frequente che il lavoro degli analisti sia ignorato dai progettisti, ed il lavoro di questi ultimi sia ignorato dai programmatori: un grado di astrazione cosi' alto da consentirci di rimanere totalmente indipendenti dalla piattaforma, sino alla fase di implementazione, e' altamente desiderabile ma anche molto rischioso: un buon progettista capisce quando e' necessario rinunciare all'astrazione per evitare ai programmatori il compito di ri-progettare il sistema.

22 L'architettura banale Si puo’ essere tentati di trasferire direttamente il risultato dell'analisi dallo spazio del problema a quello della soluzione. in questo caso il modello del design e' identico al modello ottenuto al termine dell'analisi. Molto spesso l'architettura "banale", cosi' chiamata in quanto deriva in modo immediato e (appunto) banale dal risultato dell'analisi, e' in effetti l'architettura che viene scelta per l'applicazione; e' altrettanto vero che molto spesso non corrisponde alla migliore architettura possibile. Vi sono tuttavia molte ragioni che portano alla scelta della soluzione banale: la mancanza di tempo per considerare strutture alternative, la mancanza di esperienza dei progettisti, un ciclo di sviluppo piu' orientato all'implementazione che al design, o troppo guidato dall'interfaccia utente. Quest'ultimo caso si presenta non di rado, in questi tempi in cui il "Rapid Application Development" viene venduto come la nuova promessa dell'informatica; come il paradigma a prototipi, di cui costituisce una nuova formulazione ma di cui condivide pregi, difetti e campi di applicabilita', il RAD tende spesso a generare prodotti under-architectured, ovvero con una architettura banale, ricavata di norma mediando tra l'interfaccia utente ed il dominio del problema.

23 Il problema maggiore della soluzione banale e' in genere proprio questo: l'eccessivo accoppiamento tra le classi, che si ripercuote in una mancanza di flessibilita' e di riusabilita' del codice e del design dettagliato. Cio' dovrebbe far seriamente riflettere chi sostiene che le tecniche object oriented non mantengono "per se'" la promessa del riutilizzo: nessuna tecnica puo' riparare grossolani errori di progettazione dei componenti, ed una architettura carente impedira' il riutilizzo del codice in qualunque paradigma di programmazione. In tal senso, un programma object oriented dove i componenti siano accoppiati troppo strettamente, non e' diverso da un programma nel paradigma strutturato dove le procedure siano troppo interdipendenti: il codice non potra' essere riutilizzato perche' non e' stato progettato in modo appropriato.

24 Meno accoppiamento con le Classi Interfaccia Un passaggio del paragrafo precedente rivela in realta' una possibile alternativa: possiamo studiare una architettura che riduca l'accoppiamento, indipendentemente dal linguaggio, introducendo delle classi piu' astratte che operino da interfaccia rispetto all'implementazione. per ogni classe dell'architettura banale, introduciamo una corrispondente classe interfaccia, il cui compito e' appunto di astrarre l'interfaccia della classe (i suoi metodi pubblici) dall'implementazione concreta dei metodi stessi. A livello implementativo, le classi interfaccia non implementano i metodi, ma (usando gli appositi costrutti del linguaggio) delegano alla classe implementazione la gestione degli stessi.

25 Scalabilita' con gli Advise Sink Quando ci troviamo di fronte a numerosi oggetti, anche la soluzione precedente non e' particolarmente indicata: la tecnica delle classi interfaccia farebbe raddoppiare un gia' consistente numero di classi, e non risolverebbe completamente il problema dell'accoppiamento sulle istanze; dimensioni, e difficolta' di testing. Concetto non semplice. Di norma vengono evidenziate tutte le classi che hanno tra di loro rapporti di client-server. A fronte di cio’ si devono tenere in considerazione i meccanismi di comunicazione tra loro. Quindi per queste classi non sara’ necessario applicare una tecnica di disaccoppiamento, ma sara’ sufficiente implementare una buona struttura di advise-sink.

26 Stratificazione Una delle caratteristiche di un buon design è quella di separare elementi posti a diversi livelli di astrazione; questa caratteristica viene comunemente indicata come stratificazione, intendendo così che ogni sottosistema può essere visto come un insieme di strati, di livello sempre più astratto, ognuno dei quali utilizza, almeno in linea di principio, solo i servizi dello strato sottostante. Un esempio ben noto di architettura stratificata, indicata in questo caso anche come "a cipolla", è quella di molti sistemi operativi: al più basso livello di astrazione abbiamo l'hardware, al più alto le applicazioni, ed in mezzo diversi strati che isolano dai dettagli dei livelli sottostanti: gestione dei task, della memoria, del file system, e così via. La stratificazione mira a ridurre le dipendenze tra gli elementi del sistema, e quindi il fenomeno delle modifiche a cascata; notiamo la differenza tra stratificazione ed incapsulazione: in un sistema object oriented, l'incapsulazione nasconde i dettagli implementativi della classe, mentre la stratificazione riduce gli accoppiamenti sui messaggi. L'incapsulazione in sé non è sufficiente a proteggere dalle modifiche a cascata, poiché modifiche all'interfaccia di una classe (che possono comunque avvenire) richiedono necessariamente un adattamento nelle classi client. L'interposizione di diversi livelli di astrazione dovrebbe, se ben strutturata, smorzare la propagazione delle modifiche verso i livelli successivi. Come si applica il concetto di stratificazione a livello di design object oriented? In gran parte, osservando le connessioni di messaggio, a livello delle categorie di classi, ed all'interno di queste al livello dei singoli oggetti. Da tali connessioni possiamo determinare un grafo diretto, dove due nodi (classi o categorie di classi) sono connessi se e solo se esiste una connessione di messaggio tra un oggetto di una classe (o categoria di classi) e l'altra

27 Design del dominio del problema Dettagli della struttura statica e definizione della struttura dinamica del modello. Le componenti essenziali di un modello OO: 1.Dominio del problema 2.Interfaccia utente 3.Gestione dei task 4.Gestione dei dati

28 Struttura statica Il dominio del problema è stato in gran parte identificato durante il processo di analisi, che ha evidenziato le classi rilevanti ai fini del modello, le relazioni tra esse esistenti, gli scambi dei messaggi, e così via. Perché dunque riconsiderarlo a proposito del design? Ricordiamo che attraverso l'analisi abbiamo stabilito cosa debba fare il sistema; è ora importante definire, all'interno del dominio del problema, come tali compiti verranno svolti. Ciò comporterà, l'estensione o la parziale revisione delle gerarchie di classi coinvolte, in quanto dovremo necessariamente introdurre nuovi elementi appartenenti al dominio della soluzione (o più esattamente, di una possibile soluzione tra le molteplici scelte possibili). Passare da una descrizione del problema ad una definizione della soluzione, nell'ambito di un modello object oriented, comporta i seguenti passi: la definizione degli algoritmi fondamentali, con le strutture dati o le classi associate, quando necessario. l'introduzione delle cosiddette classi di infrastruttura. l'introduzione di classi aggiuntive nel dominio della soluzione. l'eventuale introduzione di classi di controllo

29 Definizione degli algoritmi. Indipendentemente dal paradigma utilizzato, la soluzione di un problema comporta sempre la ricerca e l'utilizzo di un appropriato processo di calcolo o di manipolazione dell'informazione, ovvero un opportuno algoritmo. La formulazione concreta dell'algoritmo può essere diversa, in funzione del paradigma ed anche delle potenzialità del linguaggio target, ma una formulazione astratta dell'algoritmo stesso prescinde in gran parte dal formalismo utilizzato: quicksort resta comunque quicksort, sia esso realizzato attraverso una funzione che opera su array, da un predicato in programmazione logica, o con l'ausilio di una gerarchia di classi aventi una funzione virtuale per il calcolo del minore tra due elementi. La conoscenza degli algoritmi e delle principali strutture dati (alberi, grafi, Hash table, e così via) deve far parte del bagaglio culturale di ogni programmatore o progettista: naturalmente, conoscere una formulazione nell'ambito del paradigma object oriented può essere più vantaggioso, ma è spesso sufficiente una buona conoscenza anche di una formulazione "classica", basata su abstract data types o su una visione funzionale. Scegliere un algoritmo può essere banale in alcuni casi, mentre può diventare un vero e proprio progetto di ricerca, con lo studio e la definizione di nuovi procedimenti, qualora le esigenze siano particolarmente ardue da soddisfare.

30 La scelta di un algoritmo dipende sia da motivazioni tecnologiche che dalla singola applicazione. Le “motivazioni tecnologiche" includono ad esempio l'architettura hardware a disposizione: per citare un esempio semplice, spesso occorre decidere se un dato vada memorizzato o ricalcolato ogni volta. D'altra parte, è l'applicazione stessa che determina la frequenza delle singole operazioni: ciò che potrebbe essere conveniente con una determinata distribuzione della frequenza delle operazioni, può non esserlo con un'altra. Un albero perfettamente bilanciato può avere senso se le operazioni di ricerca sono molto più frequenti di quelle di inserimento/cancellazione, ma non viceversa. Quando definiamo o comunque decidiamo l'utilizzo di un determinato algoritmo, facciamo spesso riferimento a strutture dati (ovvero, nel nostro contesto di OOD, a classi) ben note e presenti in quasi ogni programma: liste, alberi, tabelle, matrici, eccetera. Queste classi "base", che infatti sono spesso contenute nelle librerie di base dei linguaggi, formano le cosiddette classi di infrastruttura; notiamo che in alcuni casi sarà necessario arrivare comunque al design dettagliato di classi di infrastruttura, o fare riferimento ad una particolare implementazione: se la cancellazione di elementi random di una lista è un'operazione frequente, un'implementazione della lista con doppi puntatori sarà più indicata di una con singoli puntatori. In molti casi, tuttavia, possiamo introdurre nel dominio della soluzione le necessarie classi di infrastruttura senza preoccuparci eccessivamente dell'implementazione, ma definendo semplicemente opportuni vincoli che verranno poi passati al programmatore.

31 Oltre a queste classi "generiche", la nostra particolare soluzione potrà richiedere anche la definizione di nuove classi, non facenti parti a priori del dominio del problema, e che sono specializzate per la soluzione identificata. Tra le classi specializzate, un ruolo particolare è spesso riconosciuto alle cosiddette classi di controllo, che interfacciano l'applicazione con il mondo esterno, inteso sia come sistemi di I/O che come interfaccia utente. Per esempio, nel paradigma model/view/controller, i "dati" dell'applicazione e l'interfaccia utente non sono direttamente collegati, ma l'interazione passa attraverso classi intermedie (controller); questo approccio ha il vantaggio di diminuire l'accoppiamento con l'interfaccia utente, al prezzo di una codifica più complessa. Per questa ragione, molti framework commerciali si basano su un paradigma model/view, con controller fissato o embedded, piuttosto che sul model/view/controller.

32 Specificare le classi Tutte le classi di cui sopra verranno aggiunte al nostro modello object oriented, arricchendo di conseguenza il risultato dell'analisi; il design non si ferma naturalmente qui, in quanto è ora necessario scendere nel dettaglio di ogni singola classe e definire gli elementi lasciati in sospeso durante la fase di analisi o i passi precedenti. In particolare, il risultato finale del design deve comprendere: la definizione completa dell'interfaccia di ogni classe. Questo significa definire, per ogni metodo esportato dalla classe stessa, il tipo del risultato e dei parametri, se si tratta di un metodo const, se il metodo è virtuale o meno. la definizione completa dei dati privati di ogni classe. Questo può includere una descrizione dettagliata di strutture dati complesse o inusuali, definizione di algoritmi di manipolazione, e così via.

33 la definizione dei metodi privati e protetti delle classi, non necessariamente completa dei dettagli implementativi, ma comprendente almeno gli elementi fondamentali per la comprensione della soluzione proposta. La descrizione per ogni metodo(almeno i piu’ complessi), dell‘ algoritmo associato. Ciò può essere ottenuto in modo più o meno formale, sia utilizzando pseudocodice che formalismi grafici o linguaggi di specifica. In genere, ha poco senso utilizzare flowcharts: se avete chiaro un linguaggio target, utilizzate uno pseudocodice basato su di esso. L'uso di linguaggi di specifica è di solito limitato ai progetti con requisiti di correttezza molto stringenti.

34 Tutti i vincoli che ha senso introdurre nel modello, incluse precondizioni e postcondizioni relative ai singoli metodi o invarianti relativi allo stato dell'oggetto o ai singoli passi di un processo di calcolo. Precondizioni, postcondizioni ed invarianti sono espressioni logiche, utilizzate per formalizzare proprietà di oggetti o funzioni: le precondizioni si utilizzano spesso per delimitare maggiormente lo spazio dei valori che possono essere assunti dai parametri dei metodi, mentre le postcondizioni sono di norma riferite allo stato di un oggetto dopo l'invocazione di un metodo, per quanto si possano utilizzare anche internamente alla specifica di un algoritmo, per chiarire lo stato di alcuni stadi intermedi.. I vincoli possono essere espressi in modo più o meno formale, spaziando dal linguaggio naturale allo pseudocodice fino all'uso di un linguaggio di specifica

35 Infine, va ricordato che il livello di dettaglio a cui potete arrivare in fase di design è spesso arbitrario: in molti casi, una definizione dell'interfaccia pubblica e una visione ad alto livello degli algoritmi sarà più che sufficiente, salvo i casi più complessi in cui è necessario definire in modo più preciso le strutture dati coinvolte e formulare un algoritmo con maggiore precisione. Come sempre, è difficile dire "quando fermarsi", poiché dipende molto dall'organizzazione: se chi esegue il design è anche incaricato di implementare il codice relativo, può trovare utile lasciare il design dettagliato ad un livello piuttosto astratto, e passare all'implementazione. D'altra parte, se il design e l'implementazione vengono eseguiti in tempi e/o da persone differenti, puo’ essere necessario fornire maggiori informazioni in fase di progettazione, onde evitare errate interpretazioni. Non è insolito che, nel caso del singolo sviluppatore incaricato di un progetto, il design e l'implementazione si fondano in un processo unico, in cui è difficile identificare dove termini uno e cominci l'altro; ciò non costituisce in sé un problema, ammesso che esistano comunque dei "documenti di design" cui fare riferimento, e non si debba invece estrarre e ricostruire tutta la conoscenza dal codice. Molte scelte, non solo di livello architetturale, ma anche riferite alle singole decisioni a livello di strutture utilizzate, sono difficili o impossibili da ricostruire dal codice: non è quindi raro che decisioni iniziali, mai documentate, vengano implicitamente mantenute in future versioni, anche a fronte di modifiche dei requisiti, semplicemente perché nessuno ne ricorda le motivazioni. Se le frequenze delle operazioni cambiano, ad esempio, potrebbe avere senso riconsiderare le strutture dati coinvolte: non di rado, tutttavia, non esiste traccia di come esse siano state scelte nella prima implementazione. Se nel vostro processo di sviluppo vi è poco spazio per il design, cercate almeno di motivare le scelte eseguite come guida per chi dovrà comprendere e modificare in seguito il vostro codice.

36 Struttura dinamica Una volta definite le interfacce delle classi ad un adeguato livello di dettaglio, il che può includere la definizione delle strutture dati e degli algoritmi fondamentali che dovranno svolgere i compiti relativi, abbiamo terminato il progetto della parte statica del sistema. Ovviamente vi saranno in seguito dei raffinamenti, in quanto il processo di sviluppo è per natura iterativo, ed è spesso necessario riconsiderare alcune scelte mentre il sistema evolve. D'altra parte, la definizione statica del sistema non è sufficiente a caratterizzare il comportamento del sistema stesso: essa si concentra troppo sulla singola classe, con un'enfasi particolare sulla visione esterna della classe stessa, e non pone nel giusto rilievo le interazioni tra le classi e gli eventuali vincoli temporali che esse comportano. Inoltre, talvolta un oggetto può trovarsi in diversi stati ben determinati, che influenzano in modo molto forte il suo comportamento. In genere, è difficile comprendere le transizioni di stato da una descrizione statica del sistema, anche se con un adeguato livello di dettaglio è spesso possibile. Si ricorre pertanto a descrizioni più espressive, sia per le interazioni tra oggetti che per la rappresentazione degli stati di un oggetto: queste fanno parte del dominio della cosiddetta struttura dinamica del sistema, che tuttavia non si limita a questo. Una parte della struttura dinamica è rappresentata dalla gestione dei task, che vedremo in seguito.

37 Diagramma degli stati Ad ogni istante, un oggetto si trova in uno stato ben determinato, definito dal valore delle sue variabili di istanza. In alcuni casi, il comportamento dell'oggetto dipende pesantemente dallo stato in cui si trova, o da alcune delle sue componenti. Notiamo che la distinzione tra risultato e comportamento può essere molto sottile in alcuni casi, e dipende strettamente da come vengono definiti l'uno e l'altro. Quando un oggetto si comporta in modo molto diverso in funzione del suo stato, lo stato diventa un elemento molto rilevante per comprendere il funzionamento dell'oggetto, ed è in genere importante documentare in modo preciso come avvengono le transizioni di stato, e quali conseguenze tali transizioni abbiano sull'oggetto stesso e sul mondo esterno. In tal caso si utilizza in genere un diagramma degli stati, ovvero un grafo diretto dove i nodi rappresentano gli stati e gli archi le possibili transizioni tra gli stati. Gli archi sono in genere etichettati per rappresentare lo stimolo che scatena la transizione (proveniente in genere dal mondo esterno, anche se talvolta ha senso considerare eventi interni al sistema).

38 Gestione dei dati Modifiche che la fase di design introduce a livello dei dati: normalizzazione ed ottimizzazione delle relazioni, persistenza dei dati e delle diverse soluzioni per ottenerla, Stutture database object oriented a tecniche più tradizionali di serializzazione dei dati.

39 Persistenza dei dati Le considerazioni e le scelte di persistenza dati si applicano al momento della definizione degli attributi e delle classi, all'interno della fase di design; esse influenzeranno direttamente sia l'esistenza di alcune classi che il layout interno di altre. Rimane comunque in sospeso un problema molto sentito, ovvero come memorizzare gli oggetti su disco e come recuperarli: questo problema, che viene in genere detto di "persistenza dei dati", è di soluzione tutt'altro che immediata, e può essere affrontato a due differenti livelli: 1.Utilizzando un database orientato agli oggetti; in questo caso, la persistenza degli oggetti è garantita dal database stesso. I database object oriented sono relativamente nuovi, e non vi sono molti prodotti commerciali disponibili; in ogni caso, esistono sia prodotti stand-alone (equiparabili ad un tradizionale package con funzionalità di database) sia librerie che possono essere utilizzate con linguaggi tradizionali. 2.Implementando una soluzione all'interno di un normale linguaggio object oriented; in questo caso, dovremo in genere definire manualmente, per ogni classe, alcuni metodi che si occuperanno della gestione specifica dei nostri dati ed appoggiarci a meccanismi gia’ presenti (es. Objectlibrary ADO).

40 Serializzazione Rimanendo all'interno dei linguaggi tradizionali, un metodo molto semplice per memorizzare un oggetto consiste nel "serializzarlo", ovvero emettere in un formato memorizzabile su file o DB la sequenza (serie) dei suoi campi. L'operazione è invertibile per recuperare gli oggetti. Questa soluzione lascia scoperti numerosi problemi, che possono essere affrontati in modi diversi: come gestire i puntatori e lo sharing di oggetti in fase di memorizzazione come gestire i puntatori e lo sharing di oggetti in fase di lettura I puntatori non possono in genere essere memorizzati come tali, in quanto non vi è nessuna garanzia che ricaricando gli oggetti il precendente valore del puntatore sia valido e punti alla corretta area di memoria. A questo proposito esistono diverse soluzioni, tra cui: utilizzare uno pseudo-puntatore, ovvero un indice di riferimento all'oggetto puntato che viene memorizzato altrove. utilizzare uno schema pseudo-relazionale, memorizzando la chiave dell'oggetto puntato. Osserviamo che i due approcci coincidono se consideriamo l'indice come chiave dell'oggetto puntato; in entrambi i casi, è comunque necessario avere a disposizione un meccanismo di ricerca di un oggetto dato un indice o una chiave; per esigenze di prestazioni, ciò comporterà l'uso di tutto l'apparato ben noto di tecniche di indicizzazione, cache, e così via, oltre agli eventuali problemi di accesso concorrente e di sharing dei dati Si tratta comunque in genere di codice che può essere posto in una libreria di classi e riutilizzato senza sforzo nei singoli casi.

41 Task & Threads I principali fattori che influenzano la progettazione orientata agli oggetti, quando la piattaforma target disponga di funzionalità di multitasking o multithreading. La migliore architettura può essere ottenuta solo valutando con attenzione i possibili vantaggi offerti dal sistema operativo sottostante. I sistemi operativi multi-tasking sono ormai molto diffusi; in molti casi, è anche disponibile una forma di "task all'interno del task", chiamato normalmente thread. Esistono anche librerie per lo sviluppo di programmi multi-threaded su piattaforme single-task come l'ormai vetusto MS-DOS. L'utilizzo di queste funzionalità del sistema operativo prescinde in gran parte dal paradigma di sviluppo utilizzato; tuttavia, se progettiamo un sistema che verrà ospitato su una piattaforma che consente il multitasking e/o il multithreading, possiamo ottenere la migliore architettura solo valutando con attenzione i possibili vantaggi offerti dal sistema operativo sottostante.

42 Oggetti, Task e Thread La decisione più difficile da prendere correttamente è in effetti il corretto partizionamento degli oggetti, ovvero quali apparterranno ad un certo task e quali ad un altro, quali oggetti apparterranno ad un thread (o viceversa), e così via. Vi sono al proposito diverse opzioni, nonché diverse opinioni: ad esempio, non pochi suggeriscono di avere un thread per ogni oggetto. Da un punto di vista astratto, in effetti, questa posizione è largamente condivisibile: gli "oggetti" del mondo reale sono entità autonome, ed ognuna "agisce" in modo sostanzialmente indipendente, anche se talvolta in coordinazione e cooperazione con altri oggetti. Ciò non significa che sia sempre la scelta migliore: anzi, in molte occasioni è possibile arrivare ad un design più efficiente, o più semplice da capire e da mantenere, solo compiendo lo sforzo necessario per individuare i livelli di task, thread, e gli oggetti che devono agire ai diversi livelli. Pertanto, vedremo ora alcuni dei fattori che influenzano la scelta; ovviamente, come in altri casi, è impossibile restare completamente indipendenti dall'architettura hardware/software su cui il sistema stesso verrà poi implementato: ad esempio, esistono grandi differenze, sia a livello delle prestazioni nelle diverse situazioni, sia a livello dei criteri da utilizzare nel progetto..

43 Fluidità dell'interazione. Uno degli scopi da perseguire è una maggiore fluidità dell'interazione con l'utente; pertanto, ogni volta che e’ possibile isolare un insieme di attività che possono essere eseguite in modo concorrente, minimizzando così le attese dell'utente, queste costituiscono dei candidati per il partizionamento in task o thread. Esempi tipici sono la stampa, l'acquisizione dati da una periferica, attività "batch" come la compilazione o l'elaborazione di dati. Uno scenario tipico nell’ ambito di un sistema di misura e’: l’ applicazione di misura sta eseguendo il programma di misura, cio’ implica una stretta comunicazione con il controllo della macchina di misura, per impartire i comandi di movimentazione ed acquisire le coordinate-punti rilevare, nel contempo verranno attivati gli algoritmi di elaborazione matematica per gli elementi geometrici da calcolare, la visualizzazione dei risultati in formato report e l’ aggiornamento della finestra di visualizzazione coordinate, quindi anche una forte interazione con l’ interfaccia utente

44 uso di linguaggi diversi: in un sistema multi-tasking ben progettato, i vari task dovrebbero comunicare attraverso un protocollo ad alto livello. Ciò ha anche il vantaggio di permettere la codifica di task diversi in linguaggi diversi; mentre per piccole applicazioni ciò è spesso controproducente, in quanto richiede sforzi "di adattamento" ai programmatori, su applicazioni medio/grandi può essere un'ottima opportunità per utilizzare il linguaggio più adatto ad ogni compito (ad esempio, un linguaggio pensato per lo sviluppo rapido per l'interfaccia utente, un linguaggio efficiente per un back-end di calcolo numerico), nonché una possibilità di impiegare programmatori con competenze e formazione diversa. Notiamo che ciò non avviene, naturalmente, a livello di thread, che normalmente hanno accoppiamento di comunicazione più alto, e vengono scritti nello stesso linguaggio del task che li ospita. tempi di compilazione: come conseguenza delle considerazioni viste al punto precedente, anche se manteniamo lo stesso linguaggio di codifica, abbiamo un completo isolamento delle unità di compilazione per i diversi task. Nuovamente, su un grande sistema ciò può corrispondere a grandi risparmi nel tempo di sviluppo, proprio perché ogni task può essere modificato e ricompilato in una frazione del tempo necessario alla ricompilazione e soprattutto al linking dell'intero sistema. Nuovamente, questo vantaggio si applica solo ai task, non ai thread.

45 dimensione concettuale se i task sono debolmente accoppiati ed altamente coesivi, come ci si aspetta che siano, e se la comunicazione avviene attraverso un protocollo ad alto livello, è ragionevole aspettarsi che gli sviluppatori (inclusi coloro che si occuperanno della manutenzione) troveranno molto più semplice comprendere nella sua interezza il task di cui si occupano. Viceversa, un sistema monolitico, molto complesso, tende a superare le capacità dei singoli sviluppatori di comprenderlo interamente. Anche se la struttura object oriented contribuisce (soprattutto attraverso l'incapsulazione) a semplificare la comprensione del sistema, è comunque molto più semplice raggiungere un buon grado di comprensione di un programma quasi-autonomo che di una piccola parte di un software complesso sviluppato in un unico task. Un buon design di un sistema multi-tasking è spesso a sua volta stratificato, con task di "basso livello" che eseguono compiti di back-end, come la lettura dei dati, la stampa, il backup, e task via via più vicini alla visione esterna del sistema.

46 protezione della memoria: per quanto ci si sforzi di rendere robusti i propri programmi, questi tenderanno comunque a contenere degli errori; ciò è sempre più probabile a mano a mano che le dimensioni del software aumentano. In una applicazione monotask (sia single- threaded che multi-threaded) un errore a run-time può compromettere l'integrità del task stesso, che viene così terminato. D'altra parte, task diversi hanno spazi di indirizzamento separati, e di norma gli errori non si propagano tra i task (a meno di carenze del sistema operativo); pertanto, la suddivisione in più task può essere un buon metodo per proteggere alcune parti critiche (ad esempio, la lettura e lo storage dei dati) da altre meno critiche, ma più interattive, complesse, o soggette ad errori run-time. In questo modo, ci si garantisce che i compiti fondamentali del sistema non vengano interrotti da errori nei moduli secondari del sistema stesso.

47 librerie di sistema occorre prestare grande attenzione nell'uso del multi-threading se vengono utilizzare librerie di sistema o di terze parti, in quanto vi è la possibilità (se non sono di recente sviluppo) che queste non siano thread-safe; un esempio molto semplice può essere la funzione printf del C: se la sua implementazione non è thread-safe, il buffer statico interno può essere sovrascritto da una seconda invocazione prima che la precedente sia terminata. Di norma, è possibile comunque ricavare delle primitive thread-safe interponendo delle funzioni sincrone basate su semafori, o in alcuni casi sostituendo la funzione con una classe che gestisca una coda di richieste; si tratta comunque di un problema molto serio, da tenere in opportuna considerazione prima di definire una struttura del sistema multi-threaded.

48 Note architetturali In alcuni casi, la logica di sincronizzazione per i diversi thread o task non appartiene a nessuna delle classi coinvolte, ma è solo motivata da una particolare politica di gestione del sistema. Inserire la logica di sincronizzazione all'interno delle classi riduce il potenziale riutilizzo in un diverso sistema, esattamente come inserire le relazioni come membri delle classi riduce la riusabilità delle stesse. Ciò non dovrebbe stupire, se consideriamo che la sincronizzazione è un accoppiamento tra le classi, e che l'accoppiamento riduce la possibilità di riuso.

49 Interfaccia Utente Ultima fase del design, ovvero l'interfaccia con gli utenti del sistema. Progettare un'efficace interfaccia utente per un programma non è molto diverso dal progettare un cruscotto di automobile o i controlli di un apparato stereo. In tutti i casi, esistono considerazioni di immediatezza(semplicita’ d’uso), usabilità, idoneità, non-ambiguità, chiarezza, e così via, che vanno attentamente vagliate se si desidera ottenere il miglior risultato. Proprio per questa ragione, sarebbe utile per ogni progettista di interfacce, o aspirante tale, estendere la propria conoscenza dell'interazione uomo-oggetti, anche al di fuori del settore informatico

50 Modelli dell'interfaccia utente Il progetto dell'interfaccia utente dovrebbe sempre essere ottenuto come mediazione tra due modelli, spesso contrapposti: il modello concettuale dell'utente ed il modello del programmatore; lo schema risultante viene definito modello del progettista. Le caratteristiche dei diversi modelli. Il modello concettuale dell'utente è un modello mentale, molto informale, formato da un insieme di relazioni (di cui l'utente assume l'esistenza) tra un insieme di elementi. Queste relazioni sono basate sia sull'esperienza quotidiana dell'utente, sia sulla familiarità con il sistema e la somiglianza con altre applicazioni: in effetti, gli esseri umani creano dei modelli mentali per le nuove situazioni in gran parte basandosi sulla somiglianza con situazioni pregresse. Ad esempio, l'icona di un fax verrà associata all'idea di un programma o di un device per l'invio di fax, in funzione del background dell'osservatore (che ad esempio, potrebbe non avere mai visto un fax). È molto importante cercare di avvicinarsi il più possibile allo schema mentale dell'utente, perché ogniqualvolta si richiede l'adattamento dell'utilizzatore ad un nuovo modello mentale, si rischia di vedere la propria applicazione etichettata come "troppo complessa". Sfortunatamente, gli utenti non sono normalmente in grado di descrivere il loro modello mentale: in effetti, di norma non siamo neppure consci di averlo; esistono molte strategie per "scoprire" tale modello, tutte o quasi basate sui seguenti punti: classificazione degli utenti: background culturale, attitudini, aspettative nei confronti del sistema, eccetera. descrizione di un insieme di scenari reali di possibile utilizzo del sistema. analisi delle procedure (manuali o automatizzate) attualmente in uso per lo svolgimento di ogni operazione. rispetto di standard a livello di ambiente operativo o aziendale.

51 Il modello del programmatore è normalmente ben codificato ed esplicito; fanno parte del modello del programmatore le restrizioni tipiche della piattaforma target, le convenzioni di sviluppo e le linee guida del produttore, gli elementi fondamentali di human-computer interaction, e così via. Il programmatore riesce spesso a mappare un modello object oriented nella sua interfaccia in modo quasi canonico, per quanto non necessariamente ottimale. Un record di un database viene immediatamente "immaginato" come una form a livello di interfaccia utente, con tanto di bottoni per la navigazione del database. Purtroppo, la visione del programmatore non di rado si rivela troppo tecnica per l'utente finale, ed è proprio in questo senso che nasce l'esigenza di un modello che, in qualche modo, cerchi di mediare tra le esigenze, talvolta contrastanti, della visione dell'utente e del programmatore.

52 Il modello del progettista rappresenta proprio questa mediazione; i suoi elementi principali sono: gli oggetti a livello di interfaccia e le loro relazioni la rappresentazione visiva degli oggetti ("look") le tecniche ed i meccanismi di interazione ("feel") Avendo a disposizione sia i documenti di design, sia una descrizione delle attività degli utenti (come visto in precedenza), lo scopo del progettista è proprio quello di scegliere le azioni e gli oggetti più rappresentativi (non tutti gli utenti avranno infatti lo stesso modello), capire cosa va inserito nel progetto e cosa va escluso per le ragioni più varie (difficoltà eccessiva, tempi di sviluppo, esistenza di prodotti analoghi, eccetera) ed infine quali nuovi elementi debbano essere introdotti nel modello dell'utente (non necessariamente l'automazione ricalca pedissequamente le procedure manuali). Attraverso varie tecniche, il progettista deve far corrispondere ad oggetti del mondo reale alcuni oggetti virtuali, ed esporre dei meccanismi per permettere l'interazione, il più possibile semplice ed "indolore", tra l'utente e tali oggetti virtuali. Il momento fondamentale, prima della scelta delle rappresentazioni visive, è quello delle tecniche e dei meccanismi di interazione.

53 Modelli di interazione Esistono due modelli fondamentali per l'interazione uomo-macchina, che non di rado vengono utilizzati all'interno della stessa applicazione in momenti diversi. Il primo modello, chiamato normalmente action-object, è il più indicato per gli utenti inesperti od occasionali, anche se inerentemente meno flessibile. Il secondo, detto object-action, lascia molta libertà operativa all'utente, ma proprio per questa ragione è più indicato per gli esperti, che non necessitano di una impostazione rigida ma semplice da seguire.

54 Il metodo action-object Come suggerisce il nome, consiste nello scegliere l'azione da compiere, e poi l'oggetto sul quale compierla; esistono molti esempi di applicazione del modello, ad esempio l'apertura di un file all'interno di una applicazione: si seleziona prima l'azione (File-Open) e poi l'oggetto (il file da aprire). Notiamo che in questo caso il sistema ci può guidare, mostrando ad esempio i soli file corrispondenti all'applicazione in uso, posizionandosi in una directory predefinita per quel tipo di file, e così via. In generale, il metodo action-object guida il più possibile l'utente, che in seguito alla selezione di una azione si trova di fronte una serie di scelte, quasi tutte obbligate come la selezione di un oggetto o la compilazione di una form, scelte che eseguite in sequenza portano al completamento di una attività. Naturalmente, in questo caso il progettista costringe l'utente ad operare secondo la propria visione dell'attività, ovvero il proprio percorso logico per portarla a compimento; per un utente occasionale del sistema, o per utenti inesperti, i benefici di una interazione guidata passo-passo superano spesso il fastidio di trovarsi costretti in una sequenza preordinata. Ciò è tanto più vero quanto meno arbitraria è la scelta dei singoli passi di interazione.

55 Il metodo object-action Al contrario, consiste nello scegliere l'oggetto ed in seguito l'azione da compiere; esempi tipici sono i browser/explorer o i programmi di disegno vettoriale. Nel primo caso, possiamo navigare a piacere il disco, scegliendo in ogni momento l'azione da applicare agli oggetti selezionati: aprire, cancellare, spostare, eccetera. Nel secondo, possiamo selezionare un qualunque oggetto grafico ed applicare qualche azione (ruotare, ingrandire, cambiare il colore, eccetera). L'utente è totalmente libero da costrizioni: nei limiti delle funzionalità previste dal sistema, non vi sono imposizioni di sorta sulla sequenza delle operazioni da utilizzare per il completamento di una attività; l'utente esperto può adattare lo strumento alla propria forma mentale ed alle proprie abitudini di lavoro, anziché essere influenzato e guidato dallo strumento stesso. Viceversa, l'utente un pò sprovveduto si troverà di fronte così tante scelte da esserne intimidito e bloccato: non a caso, in Windows 95 esiste ora il famoso bottone "Start", che ha fatto sorridere molti utenti con un minimo di disinvoltura, ma che ha fornito a tutti i nuovi utenti un "aggancio" di partenza (action-object) verso un'interfaccia che viceversa, nel momento iniziale, era proprio del tipo object-action. Come scegliere il giusto modello? La scelta non può prescindere dal tipo di applicazione e dall'abilità dell'utente; difficilmente saremo in grado di fornire entrambe le possibilità, e dobbiamo quindi tenere presente anche la rapidità di apprendimento dell'utente sin dall'inizio.

56 Object Design Eccoci ancora alla fase di riscrittura diagrammi. Gli stessi diagrammi che sono serviti nella fase di analisi, vengono riaffrontati a questo punto per la stesura ed il completamento finale. Forti della conoscenza del sisema in tutte le sue parti si ha chiaro in mente ora come effettuare l’ organizzazione delle classi, quali saranno i metodi e gli attributi da attribuire, quali saranno gli stati delle classi e dell’ intero sistema. Per cui ora si incontrano le fasi di : –Refined Object Model –Optimisation –Detailed Interaction Diagram –Refined State Transition Diagram –Persistence

57

58

59

60 Fasi Classiche in uno sviluppo SW Raccolta e analisi requisiti Stima dei tempi Analisi Design Implementazione Testing Unit Testing System testing Accettazione Test Conversion (passaggio ad un sistema nuovo) Strategia parallela Eliminazione diretta Studio pilota Introduzione graduale Manutenzione

61 Testing. Esistono 3 tipi di test. Come da titoli: il test di piccole unita’ di progetto SW, test dell’ applicazione (parti messe insieme integrando parti piu’ piccole del sistema), test funzionale(ovvero secondo le aspettative dell’ utente), test dell’ applicazione e dell’ intero sistema (test di stress, di sicurezza, di recovery da situazioni critiche, di prestazione, velocta’, affidabilita’, ecc) Conversion. Passaggio da un sistema esistente ad uno nuovo(nuova versione). Si puo’ effettuare in modo: parallelo(i sistemi coesistonoper un po’ di tempo); eliminazione diretta(il vecchio sistema e’ completamente e definitavamente sostituito)(es. sistema di misura mantiene l’ esecuzione di programmi scritti con vecchi linguaggi); studio pilota (il nuo sistema e’ introdotto solo in certe aree); introduzione graduale. Maintenance. Il sistema deve essere operativo anche nelle fasi di manutenzione.E’ molto interessante sapere che: circa il 20% del tempo e’ relativo alla fase di debug+soluzione bachi; un altro 20% riguarda cambiamenti dati e strutture, ma il 60% riguarda nuovi sviluppi a seguito di suggerimenti utente, documentazione, e ricodifica per migliorare l’ efficienza del sistema.


Scaricare ppt "OOAD - Seconda parte.Design (progettazione) Progettazione ad oggetti Cosa significa progettare un sistema ad oggetti? La progettazione è una fase fondamentale,"

Presentazioni simili


Annunci Google