• Non ci sono risultati.

III. Progetto di un tool per la modellazione UML

N/A
N/A
Protected

Academic year: 2021

Condividi "III. Progetto di un tool per la modellazione UML "

Copied!
46
0
0

Testo completo

(1)

III. Progetto di un tool per la modellazione UML

In questo capitolo si espone il lavoro fatto per progettare uno strumento per la creazione di modelli espressi nella notazione specificata dallo UML.

Intento

Si è voluto progettare un’architettura di un tool per la modellazione di sistemi software basato sulla notazione dello UML, versione 1.4, e portare a termine la progettazione e la realizzazione del suo modulo principale: in esso si fornisce all’utente uno strumento per la creazione di diagrammi di classe il cui modello è memorizzato e costruito attraverso la libreria CppUML discussa nel precedente capitolo. L’architettura deve essere tale che possa essere sviluppata per incrementi e che abbia come obiettivo finale di divenire un “CASE tool”.

I CASE tool sono quelle applicazioni che hanno lo scopo di assistere il processo di produzione del software, fornendo allo sviluppatore una serie di strumenti, in grado di facilitare lo svolgimento di alcuni lavori. La parola “CASE”

è l'acronimo di “Computer Aided (o Assisted) Software Engineering”. Alcuni

CASE tool supportano tutte le fasi di un processo produttivo, tuttavia la maggior

parte di essi fornisce sostegno solo per alcune fasi, in particolare quelle di analisi,

progettazione ed implementazione. Nel corso degli anni sono state definite alcune

caratteristiche che un CASE tool ideale dovrebbe fornire. Ne riportiamo una breve

descrizione di seguito.

(2)

Caratteristiche architetturali

Utilizzo di interfacce standard: poiché la maggior parte dei CASE tool non supporta tutte le fasi del processo produttivo e anche perché i progettisti che lavorino su diversi tool hanno bisogno di scambiarsi dati, sarebbe auspicabile che applicazioni diverse possano condividere informazioni mediante un formato di scambio comune.

Espandibilità: un CASE tool dovrebbe basarsi su un'architettura aperta che consenta di aggiungere nuove funzionalità senza dover ricorrere ad una reimplementazione.

Portabilità: se un CASE tool è in grado di funzionare su piattaforme differenti, è possibile cambiare ambiente di sviluppo ed utilizzare ancora quel tool, evitando di dover imparare ad utilizzare altri strumenti.

Caratteristiche funzionali

Utilizzo di un repository: uno degli strumenti più importanti di un CASE tool è il repository (o dizionario dei dati). Esso contiene tutti i dati relativi agli elementi che appartengono al sistema che si sta progettando.

Consistency checking: se esiste un dizionario dei dati, è possibile verificarne la consistenza attraverso l'analisi semantica dei dati contenuti in esso. L'analisi verifica che tutte le dipendenze, le relazioni, i vincoli e le condizioni imposte nel sistema siano rispettati.

Report generation e screen generation: sono strumenti detti di “rapid

prototyping”, che permettono di generare il codice in maniera automatica per

(3)

realizzare alcune porzioni del sistema, come la creazione di report (report generation) o le interfacce per l'inserimento di dati (screen generation).

Supporto per notazioni grafiche: un CASE tool dovrebbe fornire il supporto per le notazioni più comuni, in particolare UML.

Supporto per il riuso: un CASE tool dovrebbe mantenere delle librerie di oggetti e componenti che possano essere facilmente integrati col sistema che si sta progettando.

Controllo delle versioni: durante il ciclo di vita di un prodotto vengono prodotte diverse versioni del sistema. Risulta quindi indispensabile uno strumento per la gestione delle versioni, delle revisioni e delle variazioni.

Generazione di documentazione: è di fondamentale importanza fornire la possibilità di generare la documentazione del progetto elaborato con un CASE tool. Meglio se lo stesso tool assiste il progettista a creare, organizzare e rintracciare la documentazione.

A questo punto si può introdurre il progetto ad alto livello di uno strumento

visuale per la creazione di diagrammi di classe che crei, per ogni diagramma

disegnato, il corrispondente modello attraverso le istanze delle classi della libreria

CppUML. La progettazione di tale modulo è stata fatta in modo da poterlo

espandere fino ad ottenere uno strumento CASE completo.

(4)

3.1 Progetto di un modellatore UML

Nei successivi paragrafi vi è il progetto di uno strumento visuale per la generazione di modelli UML e la realizzazione del modulo che corrisponde ad uno strumento per la produzione di diagrammi di classe e del relativo modello UML attraverso le istanze della classi della libreria CppUML.

Vediamo come si può suddividere la struttura logica di tale strumento attraverso il seguente diagramma:

Figura 3.1 – Struttura logica del tool di modellazione UML.

(5)

Si illustrano di seguito i package.

CppUML: è la libreria che ha il compito di fornire gli strumenti per creare il modello utente.

Diagrammi UML: contiene l’interfaccia grafica per l’utente (la finestra principale, le barre dei menù e dei bottoni e l’area di disegno dei diagrammi).

Inoltre astrae il concetto di diagramma UML e lo racchiude in una classe, le cui istanze si occupano di interagire con l’utente per creare un diagramma.

Document: memorizza e ordina tutti i documenti prodotti.

Libreria Qt 2.3.1: è una libreria grafica che contiene la definizione di tutti gli oggetti per creare l’interfaccia con l’utente.

Modellatore UML: contiene lo scheletro di tutta l’applicazione e definisce, oltre alla struttura grafica della finestra principale, anche tutta una serie di funzioni per gestire le varie richieste dell’utente.

Modello utente: contiene le strutture dati che permettono all’utente di creare ed editare un modello UML, indipendentemente da come viene rappresentato graficamente. Per fare questo si utilizzano le librerie CppUML. L’utente non interagisce direttamente con questo package, ma indirettamente: mentre disegna i diagrammi attraverso l’interfaccia grafica, vengono creati gli oggetti che fanno parte del modello utente.

OCppUML: comprende tutte le classi che hanno il compito di visualizzare

all’utente gli oggetti della classe CppUML. Sono classi che ereditano le proprietà

grafiche dalla libreria Qt e la capacità di osservare le classi della libreria CppUML

(6)

attraverso l’interfaccia ereditata dalle classi che implementano il pattern Observer, racchiuse nell’omonimo package.

Pattern Observer: definisce le classi che implementano l’interfaccia dell’omonimo pattern che ha lo scopo di disaccoppiare i dati di un’applicazione dalle entità che hanno il compito di presentarli all’utente.

Salvataggio e ripristino modello: gestisce le operazioni di salvataggio e di ripristino del modello costruito dall’utente attraverso un formato proprio dell’applicazione.

Stampa diagrammi: contiene il codice che gestisce la stampa dei vari diagrammi.

Utilità: contiene la definizione di tutta una serie di funzioni globali utili in fase di disegno dei vari diagrammi.

Scelta delle librerie Qt

Per quanto riguarda i particolari delle librerie grafiche Qt si rimanda alla

documentazione ufficiale fornita dal produttore [Tro]. Si citano solo brevemente

gli aspetti principali: Qt è una libreria in C++ multipiattaforma, infatti le librerie

Qt sono presenti per MS/Windows (95, 98, Me, NT, 2000) e Unix/X11 (Linux, Sun

Solaris, HP-UX, Digital Unix, IBM AIX, SGI IRIX), che fornisce agli

sviluppatori di software tutte le funzionalità per costruire una interfaccia grafica

per l’utente completa e coerente con le tipologie delle GUI più frequentemente

usate. Qt è orientata agli oggetti, facilmente personalizzabile e permette di

programmare ogni singolo componente. Vi è la presenza di un potente

(7)

meccanismo di comunicazione fra gli oggetti della libreria, o fra quelli definiti dall’utente che ne ereditano l’interfaccia, che viene realizzata tramite due nuove parole chiave: signal e slot. Queste se usate all’interno della dichiarazione delle classi definite dall’utente indicano una parte di codice dove si dichiarano i segnali e le relative istruzioni che hanno il compito di permettere la comunicazione tra gli oggetti; uno slot infatti non è altro che una funzione membro di una classe, che appartiene alla gerarchia di generalizzazione delle classi della libreria Qt, preceduta da tale parola chiave. Un oggetto grafico A, a seguito di un evento generato dall’utente (ad es. click del mouse), lancia un signal (che sarà opportunamente associato all’evento click del mouse). A questo punto andranno in esecuzione tutti gli slot (dell’oggetto stesso o di altri oggetti) che sono associati a quel signal tramite un’istruzione connect; una tale istruzione permette a tempo di esecuzione di associare un signal, emesso da un oggetto A appartenente ad una classe X ove tale signal è dichiarato, ad uno slot di un oggetto B appartenente ad una classe Y ove tale slot è dichiarato; il formato di questa istruzione è:

connect(&A, SIGNAL(signal_of_X()), &B, SLOT(slot_of_Y()));

L’estensione del C++ con queste nuove parole chiave è possibile grazie a un precompilatore fornito dalle librerie Qt che trasforma i nuovi meccanismi in funzioni standard C++.

Sempre a tempo di esecuzione si può disaccoppiare il signal di un oggetto A

dallo slot di un oggetto B con un’istruzione disconnect di simile formato.

(8)

Le classi appartenenti a questa libreria sono tutte identificate dal prefisso Q (ad es. la classe che implementa un rettangolo si chiama QRect).

In questa libreria sono predefinite le principali interfacce utente delle applicazioni a finestre e sono personalizzabili sia nello stile di visualizzazione (MS-Windows, OSF Motif, …) sia nella loro struttura: infatti si possono dotare di una serie di oggetti, predefiniti sempre dalla stessa libreria, tipici delle applicazioni WYSIWYG (bottoni, combo box, tool bar, finestre di testo,…).

Sono presenti delle classi QAction e QActionGroup che semplificano la creazione dei controlli delle finestre. Una classe QAction astrae un’azione dell’interfaccia grafica che può apparire sia nei menu che nei tool bar. Una classe QActionGroup raggruppa delle azioni: si semplifica così l’annessione e la rimozione di gruppi di azioni da istanze di menù con una singola istruzione.

Vi sono molte altre caratteristiche che mostrano la completezza di questa libreria, ma non sono oggetto di questa discussione. Quando sarà opportuno si spiegheranno le caratteristiche delle classi della libreria Qt usate.

3.2 Progetto in dettaglio

Si è vista la convenienza di disaccoppiare l’interfaccia grafica dal modulo che

contiene e gestisce i dati usando il pattern MVC ed Observer. Quindi si può

adottare lo stesso pattern anche per il tool di modellazione UML. Nel diagramma

dei package (Figura 3.1) vi è infatti il package Modello utente, che si occupa di

costruire il modello UML dell’utente, e il package Diagrammi UML, che

(9)

rappresenta graficamente lo stesso modello utente: la presenza di questa suddivisione dei compiti nasce proprio dall’intenzione di applicare il pattern Observer al tool di modellazione UML. Vediamo ora il diagramma delle classi che specifica la struttura di base del package Diagrammi UML.

Figura 3.2 – Diagramma delle classi principali del modellatore UML.

Si illustrano di seguito le singole classi.

• QmainWindow: appartiene alla libreria Qt e fornisce una tipica finestra di

applicazione con una barra di menù, qualche barra di tool (bottoni, combo box, … , che eseguono controlli) ed una barra di stato.

• Case: fornisce la finestra principale con cui l’utente interagisce. In questa

classe vengono dichiarate tutte le azioni (QAction) delle barre di menù e di

tool che essa stessa fornisce. Contiene tutti i controlli che l’utente usa per

(10)

gestire i diagrammi (crea, salva, chiudi, rinomina … diagramma), tutti quelli che operano sui diagrammi stessi (crea una classe, un’associazione, …) e quelli per il modello utente (crea, salva, … , modello). Per potere gestire applicazioni MDI (multi-document interfaces) la classe Case contiene un puntatore ad un oggetto QWorkspace che pone come oggetto grafico principale; in questo modo vi costruisce intorno tutte le barre strumenti, la barra menù, ecc.. Un oggetto QWorkspace può contenere più finestre di documento, che saranno rappresentate dai vari diagrammi. La classe Case ha inoltre il compito di mantenere, con delle liste apposite, i puntatori agli oggetti del modello UML che sono creati dall’utente attraverso i diagrammi; mantiene tutte le informazioni della struttura del modello attraverso istanze della classe Package e le istanze di tutte le classi del package Model Management, come definito nella specifica dello UML.

• DiaDoc: è la classe che ha il compito di creare un documento (nel nostro caso

un diagramma UML) che contiene la rappresentazione di una parte del

modello utente. Contiene un riferimento ad una classe QCanvas che ha il

compito di costruire un’area grafica astratta: il costruttore di DiaDoc crea un

oggetto QCanvas che contiene tutti gli oggetti che rappresentano gli oggetti

della classe CppUML e che vanno così a costituire il diagramma; questo fatto

è chiarito in seguito. Vi è inoltre un puntatore all’oggetto attivo al momento,

ovvero l’ultimo oggetto con cui l’utente ha interagito.

(11)

• DiaView: è la classe che ha il compito di osservare un oggetto tipo DiaDoc

tramite il protocollo definito dal pattern Observer e presentarne una vista all’utente; infatti DiaDoc è la classe soggetto della classe DiaView osservatrice. Per rappresentare all’utente i cambiamenti che si avverano nell’oggetto DiaDoc che sta osservando ha un puntatore View ad un’istanza della classe MyView che rappresenta visualmente il diagramma.

• MyView: contiene un riferimento all’oggetto QCanvas creato dal costruttore

di DiaDoc. La classe QCanvas definisce un’area astratta dove vengono creati e distrutti oggetti tipo QCanvasItem o sottoclassi di questa. La classe QCavasItem definisce un oggetto grafico astratto su di un QCanvas, un oggetto QCanvasItem può essere mosso nelle direzioni della larghezza e dell’altezza dell’area e può cambiare profondità, permettendo in questo modo che un oggetto nasconda l’altro se sovrapposto; per un oggetto QCanvasItem vi sono diversi metodi per gestire la sua dimensione, la sua staticità o dinamicità, il rilevamento della sua collisione o sovrapposizione con altri oggetti o con il puntatore del mouse in corrispondenza di determinati eventi (click, doppio click, movimento, ecc.), la sua identificazione, ovvero a quale sottotipo appartiene, attraverso un metodo rtti() e infine un metodo che restituisce un puntatore all’oggetto QCanvas che lo contiene. Un oggetto QCanvas che contiene molti oggetti QCanvasItem ha i seguenti vantaggi rispetto ai tradizionali oggetti grafici forniti dalla libreria:

o gli oggetti rettangolari sono disegnati più velocemente;

(12)

o gli oggetti occupano meno memoria;

o si possono fare test molto efficienti per la collisione fra oggetti;

o la ricerca di un oggetto in un’area è efficiente (si possono creare con singoli comandi le liste di riferimenti a quegli oggetti che collidono con una certa coordinata dell’area QCanvas o che collidono con un oggetto, ecc.).

La classe MyView è un sottotipo della classe QCanvasView: pertanto essa stessa è un oggetto grafico che rappresenta all’utente un oggetto QCanvas e permette allo stesso utente di interagire con gli oggetti ivi contenuti attraverso i metodi che gestiscono gli eventi di questa classe (click, doppio click, rilascio tasto sinistro, movimento del mouse, …), opportunamente ridefiniti. Nel suo insieme un oggetto MyView usa l’oggetto QCanvas, corrispondente al diagramma contenuto nel DiaDoc, e lo rappresenta all’utente; in questo modo la classe MyView ha la funzione di rappresentare un diagramma (infatti è l’istanza della classe MyView ad essere contenuta nell’oggetto QWorkspace, che permette l’uso contemporaneo di più documenti grafici) ed inoltre ridefinisce tutti i metodi per il controllo degli eventi mouse per gestire opportunamente gli oggetti ivi creati (questi fanno parte della categoria di oggetti definiti nel package OCppUML e sono sottotipi di una classe della gerarchia con a capo la classe QCavasItem;

questi hanno il compito di rappresentare un oggetto della libreria CppUML). Nella

classe MyView vi è un insieme di variabili di stato booleane che modificano,

attraverso istruzioni condizionali, il comportamento dei metodi che gestiscono gli

(13)

eventi (ad esempio la variabile ismoving, se ha valore true, afferma che si sta spostando un oggetto). Nella classe MyView ci sono tutti i metodi che creano e modificano gli oggetti grafici contenuti nell’area rappresentata.

Questa struttura è stata così articolata per permettere la massima flessibilità dell’applicazione e per rendere possibile una realizzazione di successivi prototipi che si avvicinino per incrementi ad una versione dell’applicazione completa di tutte le funzionalità richieste ad un modellatore UML.

Nel complesso, un modellatore UML che si fonda sul package Diagrammi

UML fornisce all’utente un’interfaccia per la creazione e la modifica di diagrammi

UML. L’utente dell’applicazione costruisce il proprio modello UML attraverso la

creazione dei diagrammi. Ogni diagramma è formato da tre oggetti principali di

tipo DiaDoc, DiaView, MyView; il primo oggetto ha il compito di creare oggetti

della classe CppUML (questi sono rappresentati dalle icone grafiche appartenenti

al package OCppUML che l’utente crea nel diagramma) che vanno a formare il

modello UML; non importa costruire liste che mantengano i riferimenti agli

oggetti del modello utente contenuti nel diagramma: infatti un oggetto DiaDoc

contiene un riferimento all’area QCanvas dove vengono piazzate le icone che

rappresentano tali oggetti. Un’area QCanvas può restituire con metodi propri la

lista di tutti gli oggetti ivi contenuti: ovvero quelli che fanno da icona ad un

oggetto del modello e ne sono anche osservatori, quindi a causa del pattern

Observer ne mantengono un riferimento facilmente reperibile estendendo

l’interfaccia di tali osservatori.

(14)

Il secondo oggetto (DiaView) osserva i cambiamenti nel primo e li rappresenta attraverso il terzo oggetto (MyView). Quest’ultimo è la visualizzazione del diagramma QCanvas e l’utente interagisce con esso. I simboli bidimensionali, che hanno il compito di rappresentare il modello UML costruito con istanze di classi della libreria CppUML, sono visualizzati da questo oggetto, ma sono sempre contenuti nell’oggetto QCanvas. In sostanza, un utente che aggiunge un nuovo elemento nel modello che sta progettando attraverso un diagramma, attiva un’interazione fra i suddetti tre oggetti e questa interazione ha come frutto la creazione di un oggetto della classe CppUML che definisce il modello UML dell’elemento inserito, del suo osservatore (ovvero il simbolo bidimensionale che rappresenta secondo la notazione UML tale elemento del modello) e la registrazione dell’uno all’altro secondo il protocollo descritto a pagina 97.

Gli osservatori delle classi della libreria CppUML sono definiti nel package OCppUML. Questi sono costituiti da classi sottotipo della classe Observer e sono a loro volta appartenenti a una gerarchia di classi con a capo QCanvasItem, o hanno come propri componenti classi appartenenti ad una tale gerarchia (abbiamo visto in figura 2.8, a pagina 93, un soggetto concreto Class della libreria CppUML e un osservatore concreto OClass appartenente al package “OCppUML” con tre componenti discendenti della classe QCanvasRectangle).

Di seguito si illustra la gerarchia predefinita dalle librerie Qt di classi

discendenti da QCanvasItem.

(15)

Figura 3.3 – Gerarchia delle classi derivate da QCanvasItem definite nella libreria Qt.

Da queste classi un utente della libreria Qt può derivare classi di oggetti personalizzati che possono essere inseriti in un oggetto QCanvas.

3.2.1 Package OCppUML

Si illustrano di seguito le classi che hanno il compito di rappresentare gli

oggetti della libreria CppUML del sottopackage Core e di rimanere aggiornate

rispetto a questi attraverso l’applicazione del pattern Observer: tale aggiornamento

istantaneo, come suggerisce il nome del pattern, porta spontaneamente a chiamare

osservatori le istanze delle classi del package OCppUML e a chiamare soggetti le

istanze delle classi di CppUML che sono gli oggetti del modello da osservare e

quindi da rappresentare. Inoltre, si è pensato di dare il nome della corrispondente

(16)

classe soggetto preceduta dal prefisso ‘O’ ad ogni classe osservatore del package OCppUML.

La descrizione dettagliata di come rappresentare graficamente queste classi si può trovare nel capitolo 3. di [UMLS] dal titolo: “UML Notation Guide”. I diagrammi che seguono non riportano tutti i dettagli di ogni classe, ma solo quelli che sono stati ritenuti significativi per la comprensione della loro struttura logica.

Per approfondire gli attributi ed i metodi di tali classi si rimanda al codice sorgente.

OClass

La classe OClass ha il compito di osservare un oggetto tipo Class

30

della libreria CppUML. Si è gia visto nel paragrafo 2.4.3 come le istanze di Class e di OClass possano rimanere sincronizzate grazie al pattern Observer e al suo protocollo di aggiornamento: in particolare nella figura 2.8 si vede sulla sinistra la gerarchia di classi da Base a Class della libreria CppUML, che eredita l’interfaccia della classe Subject, e sulla destra la classe OClass del package OCppUML, che eredita l’interfaccia della classe Observer. L’applicazione del pattern Observer tra le classi di CppUML e di OCppUML avviene sempre in questo modo, quindi nei prossimi diagrammi saranno lasciati sempre meno dettagli, riguardo questo aspetto, per concentrare l’attenzione sull’aspetto grafico.

30

Class è una specializzazione di Classifier: se interessano le caratteristiche che riguardano

la specifica UML si consultino le figure 1.22 e 1.26.

(17)

Non resta che approfondire l’aspetto della grafica di un oggetto OClass: dalle specifiche della notazione UML una classe è disegnata come un rettangolo con all’interno tre compartimenti separati da linee orizzontali. Il compartimento che si trova più in alto contiene il nome della classe ed altre sue proprietà, quello centrale contiene la lista degli attributi, quello inferiore contiene la lista delle operazioni.

Di seguito si mostra un diagramma della classe OClass che illustra più in dettaglio le proprietà grafiche di questa classe.

Figura 3.4 – Le proprietà grafiche della classe OClass.

(18)

Le classi che hanno come lettera iniziale ‘Q’ fanno parte delle librerie grafiche Qt: le classi fornite da questa libreria sono state sempre specializzate prima di essere adoperate per personalizzarle ed aggiungere metodi utili alla manipolazione ed al riconoscimento delle loro istanze all’interno di un diagramma. Due sono le modifiche principali che sono state fatte alle classi usate di tale libreria; la prima è la ridefinizione della funzione int rtti() che permette di riconoscere il tipo dell’oggetto, su cui si invoca tale metodo, tramite il numero restituito (ad esempio quando l’utente preme il tasto sinistro su un oggetto del diagramma è possibile, con i metodi offerti dalla classe QCanvas, ottenere un puntatore generico objPtr di tipo QCanvasItem, all’oggetto con cui l’utente ha interagito. Per riconoscere ed adoperare poi i metodi esclusivi del tipo di oggetto puntato da objPtr, tramite un opportuno cast del puntatore, si deve invocare il metodo objPtr->rtti() che restituisce l’intero che identifica univocamente il tipo dell’oggetto); la seconda modifica è la specifica della profondità di ogni elemento grafico. In questo modo, in caso di sovrapposizione di oggetti di diverso tipo, quello più in superficie copre quello più profondo.

Tornando alla descrizione della classe OClass si nota che possiede tre

componenti per la propria visualizzazione: NameComp, AttrComp e OperComp

che corrispondono rispettivamente ai compartimenti del nome, degli attributi e dei

nomi. Tutti e tre i compartimenti sono specializzazioni della classe

QCanvasRectangle: un oggetto di questa classe è un rettangolo di cui si possono

cambiare la posizione, le dimensioni, il colore dell’interno e del bordo, lo spessore

(19)

della linea, ecc.. Ogni componente ha un riferimento owner all’oggetto OClass che lo possiede; viceversa l’oggetto OClass ha i riferimenti compname, compattr e compoper ai tre oggetti compartimento che possiede (questi riferimenti sono evidenziati, nel diagramma in figura 3.4, dal nome delle estremità delle associazioni che legano OClass ai suoi componenti NameComp, AttrComp e OperComp. Questo modo di indicare i riferimenti di una classe a quelle classi con cui è associata è una convenzione che da ora in poi si lascia sottintesa):

• compname ha i tre attributi tipo testo

31

(name, properties e stereotype) per rappresentare all’utente i corrispondenti valori dell’oggetto Class osservato;

compattr ha come attributo la struttura tipo Coda<Testo*> di nome textattr che permette di gestire un numero qualsiasi di righe di testo che rappresentano gli attributi dell’oggetto Class osservato;

• compoper ha come attributo la struttura tipo Coda<Testo*> di nome textoper

che permette di gestire un numero qualsiasi di righe di testo che rappresentano i metodi dell’oggetto Class osservato.

Segue la dichiarazione di OClass:

/*Remember: Every change to Metamodel must be done first to classes of CoreElements then The observers will be updataed by the function Notify() that's in every set- Method */

class OClass : public Observer {

public:

31

Testo è derivato dalla classe QCanvasText che permette di inserire del testo su di un

oggetto QCanvas e definirne la posizione, il testo, il carattere ed il colore.

(20)

OClass(Class* , QCanvas*);

~OClass();

int rtti() const{ return 40100;};

void Update(Subject*, Aspects needed=Nname);

virtual Base* mysubject() {return ((Base*)MySubject);}

//These are the squares that form the visual widget of a class NameComp* compname;

AttrComp* compattr;

OperComp* compoper;

// parametro* comppar; (not yet implemented)...

// return the rectangle that bounds the Class widget QRect boundingRect();

void ReBorder(int, int);

void fitToText(); // fit classifier width to text inside.

int maxwidth(); // return max width of text inside margin included QString desc;

// to resize the widget properly.

int nattr;

int noper;

void Hide();

void Show();

Coda <OAssociationEnd*> associations;

//the edges of dependencies on this Class as supplier Coda<Arrow*> arrowdependencies;

// all lines of client dependencies that start from this class as client Coda<DipLine*> cliientLines;

Coda<Arrow*> arrowsonNodeCross; //all arrows on top of clientLines void placeAend(); //to place properly squares of AssociationEnd

private:

Class* MySubject;

};

Vediamone i metodi:

• mysubject() restituisce un puntatore generico Base al soggetto osservato

(tramite la funzione getUMLName() è sempre possibile discriminare il tipo del

soggetto);

(21)

• boundingRect() restituisce un rettangolo che circonda i tre componenti

dell’istanza di OClass. Viene usato quando si sposta una classe da un punto ad un altro del diagramma per ottenere un riquadro che nasce dal contorno della stessa classe e viene spostato tramite il mouse nel punto ove si vuole posizionare la classe;

• ReBorder (int xspost, int yspost) sposta sul diagramma l’oggetto OClass

insieme ai suoi componenti e a tutti i propri attributi grafici della quantità specificata dalle coordinate relative alla posizione precedente (xspost, yspost);

• fitToText() ridimensiona i componenti dell’oggetto OClass in modo tale che circondino con le giuste distanze tutte le righe di testo che lo specificano;

• maxwidth() restituisce la larghezza massima espressa in numero di pixel del testo incluso nell’oggetto OClass;

• Hide(), Show() servono per nascondere/mostrare l’oggetto OClass.

Vi sono altri attributi e metodi che ora non vengono descritti perché riguardano la rappresentazione delle associazioni e delle dipendenze.

Si era visto il meccanismo di aggiornamento nel paragrafo 2.4.3: per approfondire il suo funzionamento, solo per questa classe, si riporta il codice della funzione Update(…) che è ridefinita per ogni classe del package OCppUML:

void OClass::Update(Subject* changedsub, Aspects needed) { if (changedsub==MySubject)

{ //const QString copyname(MySubject->Getname()) ; QFont und, bold, ibold, uitalic, italic;

ibold.setBold(true);

ibold.setItalic(true);

bold.setBold(true);

und.setUnderline(true);

uitalic.setItalic(true);

(22)

uitalic.setUnderline(true);

italic.setItalic(true);

QString appg("");

if( needed==Nname) {

if (MySubject->isAbstract())

compname->textname->setFont(ibold);

else compname->textname->setFont(bold);

compname->textname->setText(MySubject->getName().c_str());

fitToText();

}

if (needed==Nall) {

if (MySubject->isAbstract())

compname->textname->setFont(ibold);

else compname->textname->setFont(bold);

if (MySubject->isLeaf()) appg="{Leaf}";

if (MySubject->isRoot())

{ if (MySubject->isLeaf()) appg="{Leaf, Root}";

else

appg= "{Root}";

}

compname->prop_list[0]->setText(appg);

//Comp Attrib. & Oper.

Coda<Feature*> feat(MySubject->getFeatures());

feat.resetcursor();

Testo* txt;

int i,k,j,nextattribY,nextoperY, X; // k utility index i=0; //row's index of attributes' table)

int maxvettindex;

nextattribY =compattr->areaPoints().point(0).y()+1;

X=compattr->areaPoints().point(0).x()+6;

while(!feat.cursorisnull()) {

if (feat.currentitem()->getUMLClassName()=="Attribute") //Comp Attrib.

{

maxvettindex=compattr->textattr.size()-1;

if (i>maxvettindex) {

txt=new Testo(compattr->canvas());

compattr->textattr.add(txt);

txt->show();

txt->setX(X);

txt->setY(nextattribY) ; }

else

txt=compattr->textattr.get(i);

nextattribY=nextattribY+11;

(23)

i++;

appg="";

// ATTR VISIBILITY

switch ( feat.currentitem()->getVisibility()) {

case _public:

appg=appg+"+";

break;

case _private:

appg=appg+"-";

break;

case _protected:

appg=appg+"#";

break;

case package:

appg=appg+"~";

break;

};

//ATTR NAME

appg=appg+feat.currentitem()->getName().c_str() ; //ATTR MULT & ORDERING

if ( (dynamic_cast<Attribute*>(feat.currentitem())-

>getMultiplicity().getMultiplicity()) !="1") {

appg=appg+"[" ;

appg=appg+dynamic_cast<Attribute*>(feat.currentitem())-

>getMultiplicity().getMultiplicity().c_str() ;

if ( (dynamic_cast<Attribute*>(feat.currentitem())-

>getOrdering()==ordered)) appg=appg+" ordered" ; appg=appg+ "]";

} //ATTR TYPE

appg=appg+ " :";

appg=appg+dynamic_cast<Attribute*>(feat.currentitem())-

>getType()->getName().c_str();

//ATTR INIT VALUE

if ( dynamic_cast<Attribute*>(feat.currentitem())-

>getInitialValue().getBody()!="") {

appg=appg+ " =";

appg=appg+ dynamic_cast<Attribute*>(feat.currentitem())-

>getInitialValue().getBody().c_str();

}

// *************************************

//ATTR CHANG.

(24)

if (dynamic_cast<StructuralFeature*>(feat.currentitem())-

>getChangeability()!= changeable) {

appg=appg+ " { ";

switch

(dynamic_cast<StructuralFeature*>(feat.currentitem())->getChangeability()) {

case frozen:

appg=appg+"frozen";

break;

case addOnly:

appg=appg+"addOnly";

break;

};

appg=appg+"}";

} // SCOPE switch

(dynamic_cast<StructuralFeature*>(feat.currentitem())->getTargetScope()) {

case instance:

; break;

case classifier:

txt->setFont(und);

break;

};

txt->setText(appg);

} //end if feat.shiftcursor();

} //end while

//now we must delete from compattr->textattr pointers not used maxvettindex=compattr->textattr.size()-1;

k=i;

while( i<=maxvettindex) {

txt=compattr->textattr.get(k); //because I remove every cycle a pointer from the queue, so I must get at the same index!

compattr->textattr.remove(txt);

delete txt;

i++;

}

//adjusting attr heigh

nextattribY =compattr->areaPoints().point(0).y()+1;

compattr->textattr.resetcursor();

while( !compattr->textattr.cursorisnull()) {

nextattribY+= 11;

(25)

compattr->textattr.shiftcursor();

}

compattr->setSize(compattr->width(), (nextattribY-(compattr-

>areaPoints().point(0).y())+5 ));

compoper->move(compattr->areaPoints().point(0).x()+1,compattr-

>areaPoints().point(2).y()-1);

//Oper comp

feat.resetcursor();

nextoperY =compoper->areaPoints().point(0).y()+1;

Coda<Parameter*> parameters;

Classifier* clptr;

Operation* optr;

j=0; //indice riga della tabella delle operazioni (row's index of operations' table)

while(!feat.cursorisnull()) {

if (feat.currentitem()->getUMLClassName()=="Operation") //Comp Operation.

{

maxvettindex=compoper->textoper.size()-1;

if (j>maxvettindex) {

txt=new Testo(compoper->canvas());

compoper->textoper.add(txt);

txt->show();

txt->setX(X);

} else

txt=compoper->textoper.get(j);

txt->setY(nextoperY) ; nextoperY=nextoperY+11;

j++;

appg="";

// OPER VISIBILITY

switch ( feat.currentitem()->getVisibility()) {

case _public:

appg=appg+"+";

break;

case _private:

appg=appg+"-";

break;

case _protected:

appg=appg+"#";

break;

case package:

appg=appg+"~";

break;

};

//OPER NAME

appg=appg+feat.currentitem()->getName().c_str() ; //OPER parameterLIst

appg=appg+"(";

parameters=dynamic_cast<Operation*>(feat.currentitem())->

(26)

getParameters();

parameters.resetcursor();

parameters.shiftcursor();

while(!parameters.cursorisnull()) {

appg=appg+parameters.currentitem()->

getName().c_str();

appg=appg+" : ";

clptr=parameters.currentitem()->getType();

appg=appg+ clptr->getName().c_str();

if ( !parameters.currentitem()->

getDefaultValue().getBody().empty() ) {

appg=appg+" = ";

appg=appg+parameters.currentitem()->

getDefaultValue().getBody().c_str();

}

parameters.shiftcursor();

if (!parameters.cursorisnull()) appg=appg+", ";

}

optr=dynamic_cast<Operation*>(feat.currentitem());

//OPER RETURNTYPE

appg=appg+") : "+optr->getParameter(0)->getType()->

getName().c_str() ; //PROPERTIES LIST

if ( (optr->isRoot()==true) ||

(optr->isLeaf()==true) ||

(optr->isQuery()==true)||

(optr->getConcurrency()!=sequential) ) {

appg=appg+" {";

if (optr->isRoot()) appg=appg+" Root";

if (optr->isLeaf()) appg=appg+" Leaf";

if (optr->isQuery()) appg=appg+" Query";

if (optr->getConcurrency()!=sequential) switch (optr->getConcurrency()) {

case guarded:

appg=appg+" concurrency=guarded";

break;

case concurrent:

appg=appg+" concurrency=concurrent";

break;

};

appg=appg+"}";

}

// ABSTRACT && SCOPE

if ((optr->isAbstract()) && (optr-

>getOwnerScope()==classifier)) txt->setFont(uitalic);

else{ if (optr->isAbstract()) txt->setFont(italic);

else if (optr-

>getOwnerScope()==classifier) txt->setFont(und);

}

(27)

txt->setText(appg);

}//end if

feat.shiftcursor();

} //end while

//now we must delete from compoper->textoper pointers not used

maxvettindex=compoper->textoper.size()-1;

k=j;

while( j<=maxvettindex) {

txt=compoper->textoper.get(k); //because I remove every cycle a pointer from the queue, so I must get at the same index!

compoper->textoper.remove(txt);

delete txt;

j++;

}

//adjusting oper heigh

nextoperY =compoper->areaPoints().point(0).y()+1;

compoper->textoper.resetcursor();

while( !compoper->textoper.cursorisnull()) {

nextoperY+= 11;

compoper->textoper.shiftcursor();

}

compoper->setSize(compoper->width(),

(nextoperY-(compoper->areaPoints().point(0).y())+5 ));

fitToText();

} } }

Come si può notare una funzione di Update(…) è molto lunga e

particolareggiata, perché oltre a dovere aggiornare i dati rappresentati deve

cambiare lo stile della rappresentazione a seconda dei dati stessi (testo in grassetto

per il nome della classe, testo in corsivo se indica un’entità astratta, sottolineato se

lo scope dell’attributo è di classe invece che di istanza, ecc.). Questa funzione di

Update(…) ha un blocco di aggiornamento molto lungo perché è previsto anche il

parametro tipo Aspects Nall che specifica che l’osservatore deve aggiornare tutti i

dati rispetto al soggetto (questo risulta utile perché nell’implementazione del

(28)

modellatore la maggior parte delle modifiche ad una classe si fanno attraverso una finestra di dialogo alla cui chiusura aggiorna tutte le sue caratteristiche). Possono essere previsti diversi tipi di implementazione di tale funzione, ma saranno sempre caratterizzati da una certa lunghezza del loro codice; proprio per questa ragione e per il fatto che hanno struttura simile non si riportano in seguito le funzioni di aggiornamento delle altre classi “osservatore”.

OGeneralization e OGeneralizableElement

Questi sono gli osservatori rispettivamente delle classi Generalization e

GeneralizableElement della libreria CppUML. Si riporta il diagramma di classe

che presenta queste due classi soggetto concreto e queste due classi osservatore,

facendo vedere tutta la gerarchia che da Subject va alla classe Base e poi porta alle

due classi soggetto concreto per illustrare l’applicazione del pattern Observer. Per

semplificare la consultazione del diagramma è stata aggiunta una linea tratteggiata

orizzontale, che divide la parte superiore, relativa agli osservatori appartenenti al

package OCppUML, da quella inferiore, relativa ai soggetti della libreria

CppUML (i dettagli di tali classi e delle loro relazioni si possono vedere in figura

1.23). A causa della pesantezza di tale diagramma, nei successivi diagrammi, non

è riportata la gerarchia che porta agli attuali soggetti degli osservatori perché è più

facilmente consultabile nei diagrammi del package Core nella specifica ufficiale

dello UML di OMG (si vedano le figure 1.22 – 1.26), con l’unica accortezza che

nella libreria CppUML sono state aggiunte le classi Subject e Base in testa alla

gerarchia.

(29)

Figura 3.5 – Le coppie di osservatore/soggetto: OGeneralization/Generalization;

OGeneralizableElement/GeneralizableElement.

Vediamo gli aspetti grafici di questi due nuovi osservatori.

OGeneralization

OGeneralization deve osservare un oggetto Generalization che indica una

relazione fra un elemento più generale (indicato nel diagramma di figura 1.23

come parent) e uno più specifico (child): l’elemento più specifico eredita tutte le

(30)

proprietà, gli attributi, le operazioni e le relazioni da quello più generale e dovrebbe contenere informazioni addizionali. Questo tipo di relazione può avvenire fra classi, package, use case e altri elementi (infatti le precedenti classi sono tutte specializzazioni della classe GeneralizableElement).

OGeneralization è un osservatore che può rappresentare su un diagramma

una tale relazione fra due istanze di classi Class di cui mantiene il riferimento ai

loro osservatori nei propri attributi child e parent di tipo puntatore a OClass. Un

oggetto OGeneralization ha due puntatori g_child e g_parent a istanze

dell’osservatore OGeneralizableElement (tali puntatori sono indicati in figura 3.6

come i ruoli delle relazioni che legano le due classi osservatore): un’istanza di

quest’ultimo osservatore ha una semantica diversa rispetto al proprio soggetto,

infatti non è altro che un secondo osservatore di istanze di Class (in quanto

specializzazioni di GeneralizableElement). Quindi gli oggetti che entrano in gioco

quando si costruisce una generalizzazione fra due classi sono: due oggetti tipo

OClass e ciascuno di essi osserva il corrispondente oggetto tipo Class, un oggetto

OGeneralization, che osserva il corrispondente oggetto tipo Generalization, e

infine due osservatori tipo OGeneralizableElement, che osservano i due

precedenti oggetti tipo Class rappresentando esclusivamente il ruolo di padre o

figlio del proprio soggetto nella relazione di generalizzazione. Quindi nel modello

UML della corrispondente relazione vi sarà un’istanza di Generalization che lega

due istanze di Class in una relazione di generalizzazione; infatti non si possono

(31)

avere istanze della classe GeneralizableElement in quanto è astratta, ma si possono avere istanze delle sue specializzazioni.

Per poter rappresentare la relazione tra classe padre e figlio un oggetto OGeneralization possiede un cammino costituito da una o più linee consecutive che partono dall’osservatore riferito da child e arrivano all’osservatore riferito da parent; questo può avverarsi perché un oggetto OGeneralization ha un riferimento path ad uno o più oggetti tipo GenLine

32

, che disegnano il cammino, e che possiede esclusivamente (infatti sono suoi componenti). Inoltre possiede zero o più componenti G_Vertex

33

che vengono posti sulle giunzioni tra una linea e quella successiva del cammino in modo da offrire all’utente un oggetto da trascinare per ottenere il posizionamento corretto del cammino (infatti la gestione degli eventi della classe MyView si occupa di mantenere un G_Vertex solidale alla giunzione delle due linee consecutive; quindi se se ne sposta uno il cammino viene modificato di conseguenza).

OGeneralizableElement

Come già accennato vi è una differenza semantica tra un OGeneralizableElement e un GeneralizableElement. Infatti il primo è un osservatore della classe Class che partecipa ad una relazione di generalizzazione e rappresenta l’estremo di tale relazione con un vertice speciale di tipo GE_Vertex

32

GenLine deriva dalla classe QCanvasLine che rappresenta una linea su di un QCanvas.

33

G_Vertex non è altro che un vertice rappresentato come un piccolo quadrato.

(32)

e, nel caso che la classe Class abbia il ruolo di parent nella relazione, con un triangolo tipo Triangle

34

. Tali vertice e triangolo vengono piazzati all’estremo del corrispondente ruolo del cammino della generalizzazione e hanno lo scopo di unire il cammino con l’osservatore tipo OClass della classe Class che partecipa alla generalizzazione. In pratica OGeneralizableElement è un secondo osservatore delle classi Class che hanno il ruolo di padre e figlio nella relazione di generalizzazione. Invece la classe GeneralizableElement è essa stessa la classe padre o figlio di una relazione di generalizzazione: infatti un oggetto Class è una specializzazione di un GeneralizableElement (come si vede da figura 1.23).

Tornando alla notazione grafica bisogna dire che un oggetto OGeneralizableElement possiede zero o un oggetto tipo QPointArray che mantiene le coordinate dei vertici del triangolo riferito da pol ed è usato come parametro del costruttore dello stesso triangolo.

OAssociation e OAssociationEnd

Questi sono gli osservatori delle corrispondenti classi della libreria CppUML Association e AssociationEnd (il loro diagramma si trova in figura 1.23). Nel successivo diagramma di classe si mostrano le proprietà grafiche di queste due classi.

34

Triangle è una specializzazione della classe QCanvasPolygon che permette di disegnare un

poligono su di un QCanvas. Le coordinate dei suoi vertici sono mantenute in un vettore di

coordinate tipo QPointArray.

(33)

Figura 3.6 – Gli osservatori OAssociation e OAssociationEnd.

OAssociation

Questa classe ha per componenti uno o più oggetti tipo AssocLine

35

di cui mantiene un riferimento di nome path. Come nel caso della generalizzazione gli oggetti riferiti da path corrispondono al cammino di linee consecutive che legano due oggetti OClass (quindi rappresentano un’associazione tra due oggetti tipo Class). Il cammino parte sempre dal centro di una classe per arrivare nel centro dell’altra classe; la parte di cammino che si sovrappone alle due classi, che ne

35

AssocLine deriva dalla classe QCanvasLine che rappresenta una linea su di un QCanvas.

(34)

costituiscono le estremità, rimane nascosto, perché un oggetto AssocLine ha profondità più alta rispetto ai componenti di una OClass. Sulle giunzioni di due linee viene posto un oggetto tipo Vertex che è anch’esso componente di OAssociation ed è riferito con una struttura di puntatori di nome verteces.

OAssociation ha per attributo una variabile booleana di nome binary che se ha valore true asserisce che l’associazione è binaria, altrimenti è un’associazione fra più classi e con tale informazione i metodi che gestiscono la classe variano comportamento. Un oggetto OAssociation deve poter mostrare il nome dell’oggetto osservato, quindi possiede fra i suoi attributi un riferimento name che punta ad un oggetto tipo Textonwhite.

Figura 3.7 – TextonWhite è un testo fornito di sfondo bianco.

La classe Textonwhite è stata definita per offrire la possibilità di scrivere del

testo su di un QCanvas con uno sfondo rettangolare bianco della dimensione

esatta dei confini del testo; in questo modo, se tale testo si sovrapponesse a delle

linee di associazioni presenti nel diagramma, il testo rimarrebbe ben leggibile,

(35)

perché lo sfondo coprirebbe le linee retrostanti (infatti sfondo e testo hanno una profondità meno elevata rispetto agli altri oggetti grafici). Un oggetto Textonwhite è una specializzazione della classe Testo e possiede come componente un oggetto tipo QCanvasRectangle, che riferisce con il puntatore background, e che funge da sfondo per il testo.

OAssociationEnd

Osserva un oggetto AssociationEnd della libreria CppUML e rappresenta l’estremità di un’associazione che si connette ad un oggetto OClass. Ha un riferimento ad un oggetto tipo AEVertex di nome square; tale oggetto ha il compito di disegnare un piccolo quadrato sulla intersezione del cammino dell’associazione ed il bordo dell’oggetto OClass osservatore dell’oggetto Class che partecipa all’associazione: questo ornamento è stato inserito per dare la possibilità all’utente di interagire direttamente con un ruolo di un’associazione.

Un oggetto OAssociationEnd possiede due attributi tipo puntatore a Testonwhite per rappresentare le proprietà dell’oggetto AssociationEnd che osserva: mul che rappresenta la molteplicità dell’estremità dell’associazione (viene indicata solo se diversa da “1”); rolename indica il nome del ruolo dell’estremità dell’associazione. Per disegnare l’eventuale freccia, che indica la navigabilità della relazione nel solo verso indicato dalla freccia stessa, vi sono due puntatori l1 ed l2 ad ArrowLine

36

per disegnare i due tratti della freccia.

36

Arrowline è una specializzazione di QCanvasLine.

(36)

Un oggetto OAssociationEnd possiede zero o un componente tipo Lozenge

37

; questo componente serve nel caso che l’estremità della relazione corrisponda ad una classe aggregato o composizione, nei quali casi bisogna disegnare una losanga a tale estremità della relazione di colore bianco se un’aggregazione o nero nell’altro caso. Il componente QPointArray serve per costruire un componente Lozenge come nel caso di Triangle visto per le generalizzazioni.

ODependency

ODependency è l’osservatore della classe Dependency (per consultare le relazioni di quest’ultima con le altre classi della libreria CppUML si veda la figura 1.24). Nella pagina seguente si presenta il diagramma che mostra le proprietà grafiche di ODependency.

37

Lozenge è una specializzazione di QCanvasPolygon e serve a disegnare una losanga.

(37)

Figura 3.8 – Proprietà grafiche di ODependency.

Una dipendenza può sussistere fra due elementi del modello ed è rappresentata da una freccia tratteggiata che parte dall’elemento client e arriva sull’elemento supplier (ad indicare che il primo dipende dal secondo). Una dipendenza può essere etichettata con uno stereotipo ed è possibile avere un insieme di elementi client che dipendono da uno o più elementi supplier.

Il fatto che una dipendenza può sussistere fra due elementi generici del

modello è mostrato nel diagramma della specifica dello UML in figura 1.24: si

può osservare che il client ed il supplier di una dipendenza sono classi astratte tipo

ModelElement, ad indicare che una dipendenza può legare qualsiasi classe

(38)

concreta che discenda da ModelElement. Dato che non esiste un osservatore di una classe ModelElement in quanto è una classe astratta, si è ritenuto vantaggioso porre come client e supplier di un oggetto ODependency un oggetto della classe astratta Observer: in questo modo si può rappresentare una relazione di dipendenza fra qualunque classe che è specializzazione di quest’ultima e quindi fra qualunque osservatore del package OCppUML.

Si può notare che un oggetto ODependency possiede uno o più componenti supplier ed un solo client; si vedrà in seguito come è possibile rappresentare una dipendenza fra un insieme di client ed uno o più supplier.

Per disegnare la freccia, che parte dal client ed arriva sull’elemento supplier, un oggetto ODependancy possiede uno o più componenti tipo DipLine

38

riferiti con la struttura di puntatori path.

Se una dipendenza lega un insieme di client con più supplier allora ci sono più frecce che partono dai client ed arrivano su un componente dell’osservatore ODependency di tipo NodeCross

39

e da questo partono le frecce che portano ai supplier della dipendenza. In definitiva, un oggetto ODependency ha la responsabilità di mantenere un riferimento al primo osservatore di client e una

38

DipLine è una specializzazione di QCanvasLine, può rappresentare una linea ed ha la proprietà di disegnarla tratteggiata. DipLine serve quindi a disegnare la coda della freccia.

39

NodeCross definisce un vertice particolare del cammino che permette di disegnare

dipendenze fra più di un client e più di un supplier. Se si unisce un client ad un supplier non è

necessaria la sua presenza. In seguito si approfondisce questo aspetto.

(39)

struttura di riferimenti a tutti gli osservatori di supplier della dipendenza osservata; inoltre possiede un riferimento ad un oggetto NodeCross che, quando è presente, dà la possibilità di aggiungere altri client alla dipendenza; infatti un NodeCross ha per componenti degli ulteriori oggetti tipo Observer che riferisce con la struttura theClients e che corrispondono agli osservatori dei clienti aggiunti alla dipendenza.

Una freccia che rappresenta una dipendenza è costituita da due elementi: un

oggetto DipLine, con alla sua testa un oggetto tipo Arrow, che disegna con due

linee poste a ‘V’ la punta della freccia. Un oggetto Arrow ha una funzione

importante per capire la struttura di una dipendenza fra molti client e molti

supplier; infatti un oggetto ODependency memorizza in una struttura ordinata di

nome path tutte le linee tipo DipLine che costituiscono il cammino dal primo

client a tutti i supplier. Per capire come si identificano e quindi si collegano le

linee e le frecce tipo Arrow si può osservare la seguente figura che illustra la

dipendenza di alcuni client da più di un supplier. Per comodità è stato aggiunto il

numero d’ordine di inserimento nella struttura path delle linee che costituiscono la

rappresentazione della dipendenza.

(40)

Figura 3.9 – Esempio di dipendenza fra tre client e quattro supplier.

Quindi un oggetto ODependency ha per componenti due strutture ordinate:

path, che contiene i riferimenti alle DipLine (le linee tratteggiate che si vedono in

figura), ed arrows, che contiene i riferimenti alle punte di freccia posizionate sui

soli supplier (quindi gli oggetti Arrow in testa alle linee 2, 3, 5 e 7). Ogni oggetto

Arrow ha un riferimento owner alla linea tipo DipLine che lo possiede, ovvero la

linea con cui costituisce una freccia. Se si vuole ricostruire la rappresentazione

della dipendenza si prendono le due strutture path e arrows ivi contenute e si

analizzano in questo modo: se il primo oggetto in path è riferito dal puntatore

owner che si trova nel primo oggetto in arrows allora si ha una dipendenza fra un

(41)

client e un supplier con una freccia che li unisce e che è costituita da una linea ed un oggetto Arrow. In questo caso invece si ha la linea 1 come primo elemento di path e la punta di freccia che sta in testa alla linea 2 come primo elemento di arrows; dal fatto che tale oggetto Arrow non è posseduto dalla linea 1 si deduce che ci sono altre linee che costituiscono il path e quindi in testa alla linea 1 ci va il componente node che è lo speciale vertice tipo NodeCross che si mette in testa alla prima linea di un cammino costituito da più di un oggetto DipLine. In seguito si prende il secondo oggetto del path, si verifica che è riferito dal primo oggetto Arrow di arrows quindi tale oggetto Arrow va in testa alla linea 2 e tale linea unisce il vertice speciale node con “Supplier_1”. Quindi si prende il secondo riferimento in arrows e dato che esiste vuol dire che c’è un secondo supplier. Il terzo elemento di path deve partire dal vertice speciale che è la sorgente di tutti i cammini a oggetti supplier aggiunti alla dipendenza. Il secondo oggetto Arrow ha riferimento owner uguale al terzo riferimento di path, quindi tale punta di freccia va posizionata in testa alla linea 3 e deve indicare il secondo supplier. Si passa alla linea 4 di path che deve nascere dal vertice node e si riscontra che tale linea non possiede la terza punta di freccia, quindi tra la linea 4 e la 5 va posto un vertice semplice tipo D_Vertex

40

(serve per trascinare le linee 4 e 5). Si passa alla

40

D_Vertex è una specializzazione di un QCanvasRectangle, funge da vertice di due DipLine

consecutive e offre all’utente un piccolo quadrato da trascinare per modificare la posizione della

giunzione con cui è solidale. Un D_vertex contiene un puntatore beforeLine alla DipLine che lo

precede nel cammino che va dal client al supplier.

(42)

linea 5 e si vede che il terzo oggetto contenuto in arrows ha puntatore owner che punta alla linea 5, che quindi possiede tale punta di freccia e indica in tal modo il terzo supplier. Altrettanto per le linee 6 e 7.

Il vertice NodeCross è stato concepito per aggiungere qualsivogliano oggetti client alla dipendenza. Infatti l’oggetto NodeCross possiede le due strutture toClients e the Clients: nella prima sono contenuti i riferimenti ordinati alle linee 8 e 9 che uniscono il secondo ed il terzo client con il NodeCross stesso; nella seconda invece ci sono i riferimenti ordinati al secondo ed al terzo client. Per mantenere un riferimento alle punte di freccia in testa alle linee 1, 8 e 9, un NodeCross possiede una struttura sua componente di nome arrowonthis che mantiene i riferimenti alle punte di freccia dei client aggiunti che puntano su se stesso.

Per facilitare la navigazione in questa struttura complessa ogni oggetto Arrow ha due puntatori che indicano quale oggetto è puntato dal vertice della freccia:

node e infronttopArrow. Di questi due puntatori è valido quello con valore diverso

da “NULL”. Il primo indica che la punta di freccia è su di un oggetto NodeCross e

ne fornisce il riferimento, il secondo indica che la punta di freccia è su di un

osservatore di un oggetto del modello e ne fornisce il puntatore generico ad un

Observer.

(43)

3.3 Sviluppi futuri

Si può vedere ora un progetto ad alto livello delle espansioni future che si possono aggiungere al modellatore UML per ottenere uno strumento CASE completo.

3.3.1 Analisi e specifica dei requisiti

Tipi di utente

1. Progettista software: usufruisce di tutte le funzionalità del CASE tool, può creare quindi tutti i tipi di diagrammi forniti dallo UML, può presentare i diagrammi dei casi d’uso ed i documenti che specificano i requisiti del problema di design da risolvere sia al committente che ad esperti del dominio del problema e ricevere da questi un feedback.

2. Committente del software: rilascia i requisiti del software che ordina e verifica la parte di progettazione astratta che può comprendere e verificare.

3. Persone esperte dell’oggetto di design: durante l’analisi dei requisiti forniscono il proprio know how e verificano i requisiti che vengono astratti da questo.

Funzionalità

• Disegno di diagrammi UML attraverso una semplice interfaccia grafica e

conseguente creazione del modello UML del progetto rappresentato.

(44)

• Possibilità di memorizzare, attraverso un repository, tutti i documenti raccolti e prodotti, in modo da rintracciarli e ordinarli attraverso diversi criteri.

• Supporto del controllo delle versioni.

• Consistency checking.

• Generazione automatica del codice a partire dal progetto fisico.

• Esportazione/importazione del modello software progettato attraverso il formato standard XMI

41

.

• Stampa dei diagrammi.

• Salvataggio dei diagrammi UML e del relativo modello.

• Impostazione automatica del layout dei diagrammi.

Requisiti non funzionali ed ambiente di sviluppo

Come piattaforma si è deciso di utilizzare Linux, nella fattispecie sotto forma della distribuzione Mandrake 8.2, sia per motivi di stabilità che per poter sviluppare un software di tipo free. Quest’ultimo aspetto è stato considerato molto importante nella prospettiva di iniziare un progetto con lo scopo di realizzare uno strumento CASE, basato su UML, completo e non commerciale.

41

Lo scopo principale di XMI (XML-Metadata-Interchange) è quello di permettere a tool di modellazione UML di scambiarsi facilmente i modelli ed i repository nell’ambito di ambienti eterogenei e distribuiti.

XMI integra tre standard:

1. XML - eXtensible Markup Language (W3C standard);

2. UML - Unified Modeling Language (OMG standard di modellazione sistemi software);

3. MOF - Meta Object Facility (OMG standard per i metamodelli e repository di metadata).

L’integrazione di questi tre standard nello XMI unisce il meglio delle tecnologie per la

modellazione e per la costituzione di repository, permettendo agli sviluppatori che lavorano su

sistemi distribuiti di condividere oggetti, modelli ed altri metadati attraverso Internet.

(45)

Questo strumento CASE, sia per la scelta delle librerie grafiche, sia per la progettazione della libreria CppUML, è portabile anche su piattaforme tipo MS/Windows (95, 98, Me, NT, 2000) e Unix/X11 (Linux, Sun Solaris, HP-UX, Digital Unix, IBM AIX, SGI IRIX).

3.3.2 Progetto ad alto livello

Architettura

Si deve progettare un’architettura aperta in modo tale che possa essere espansa tutte le volte che se ne presenti la necessità. Il cuore delle funzionalità di uno strumento CASE risiede nella modellazione di sistemi software attraverso i diagrammi che fanno parte della specifica UML. Quindi il modulo principale si occuperà della creazione di modelli UML, attraverso la libreria CppUML, e provvederà a fornire all’utente un’interfaccia grafica che permetta la creazione, la modifica e la visualizzazione di tali modelli attraverso i diagrammi UML.

Struttura logica

Si illustra la suddivisione logica del CASE tool in moduli logici rappresentati

dai seguenti package.

Riferimenti

Documenti correlati

CORSI DI LAUREA MAGISTRALE IN SCIENZE STORICHE E IN SCIENZE DEL GOVERNO CORSI DI LAUREA TRIENNALE IN STORIA, IN SCIENZE POLITICHE E SOCIALI E IN BENI CULTURALI. DOTTORATO IN

 Si può anche usare il valore di ritorno come Si può anche usare il valore di ritorno come parametro nella chiamata di un altro metodo:. parametro nella chiamata di un

Per costruire gli oggetti si deve seguire questo procedimento: 1) disegnare i pezzi sul legno, 2) tagliare i pezzi con il seghetto, 3) unirli con la colla (o in altro modo).

Come vedi abbiamo pensato a diversi modi alternativi per utilizzare una cuffia da doccia.. Vai alla pagina successiva per vedere il

 chiedere in input un colore e calcolare e visualizzare il perimetro di tutti i poligoni contenuti nel vettore che sono di quel colore, visualizzando anche

 document:contiene le proprietà basate sul contenuto del docuumento come il titolo,i links, le form..  location: le proprietà basate

Piuttosto che duplicare 1000 volte il codice macchina di un oggetto il sistema che si occupa di istanziare gli oggetti carica un’unica copia in memoria del codice macchina comune

quando viene istanziato da un altro oggetto della stessa classe. l’argomento è una reference ad un