back to top

Da UML a C++

Una volta sviluppati un numero sufficiente di diagrammi UML, per la descrizione del nostro prodotto, bisogna passare alla fase di traduzione, per effettuarla basta seguire le seguenti regole, sviluppate in questo caso per il C++ ma facilmente adattabili a qualsiasi linguaggio di programmazione di alto livello orientato agli oggetti(come JAVA, visual C++, etc).

In generale gli strumenti CASE (Computer Aided Software Engeneering), sono in grado di costruire da soli lo scheletro del nostro prodotto a partire dai nostri grafici, comunque è sempre meglio sapere come affrontare tale traduzione manualmente per correggere eventuali errori dello strumento.

Gerarchia di Generalizzazione-Specializzazione.

Come abbiamo già visto nel capitolo 6 la gen-spec implica la presenza di una classe Base che funziona da interfaccia generale, ovvero in essa sono presenti gli attributi e i metodi comuni a tutte le classi che da esse deriveranno, le quali a loro volta conterranno ulteriori attributi e metodi con i quali sarà possibile specializzare la classe Base.

La codifica di tale gerarchia viene così effettuata:

class Base
{
    public:
        void f1();
        virtual void f2() = 0;
        virtual void f3();
    protected:
        void f4();
        // variabili membro
};

class Derivata:public Base
{
    public:
        virtual void f2();
        virtual void f3();
    protected:
        // variabili membro
};
Analizziamo il codice della classe Base:

Base::f1() è una funzione direttamente invocabile dall’utente, in quanto contenuta nella parte public della classe base.

Base::f2() DEVE essere ridefinita in TUTTE le classi derivate poichè essa è una funzione virtuale pura, ovvero è una funzione che deve essere specializzata, quindi essa non risulta essere utilizzabile dall’utente della classe Base.

Base::f3() al contrario della precedente questa funzione PUO’ essere specializzata da classi derivate ma non lo deve essere necessariamente, in quanto essa è una funzione virtuale NON pura.

Base::f4() questa funzione può essere richiamata per funzionalità comuni a classi derivate in quanto essa è inserita nella parte protected della classe, ciò implica che tale funzione verrà automaticamente ereditata da tutte le classi figlie. Insieme alla funzione f4() vengono ereditati dai figli anche tutte le variabili membro che sono qui contenute.

Analizziamo il codice della classe Derivata:

Derivata::f2() è la ridefinizione della funzione virtuale pura presente nella classe Base.

Derivata::f3() è l’eventuale ridefinizione della funzione virtuale NON pura.

Anche in questo caso nella parte protected della classe andranno messe le variabili membro proprie di quella specializzazione, così che si possa creare una nuova gerarchia di specializzazione.

Aggregazione

Vediamo ora come tradurre in C++ quanto realizzato nel capitolo 7.

Per prima cosa effettuiamo la traduzione di un generico contenimento lasco di cui segue il diagramma UML:

class Contenuto
{
    public:
        Contenuto();
        void fcontenuto();
    private:
        // variabili membro
};
Analizziamo il codice della classe Contenuto che sarà inserito nel file Contenuto.h:

Contenuto::Contenuto non è altro che il costruttore della classe.

Contenuto::fcontenuto() è una funzione pubblica della classe Contenuto.

inoltre nella parte privata vanno inserite, come al solito, le variabili membro della classe.

#include "Contenuto.h"

class Contenitore
{
    private:
        Contenuto* c;
    public:
        Contenitore(Contenuto* q):c(q){};
        void fcontenitore()
        c -> fcontenuto();
};
Tale codice può sembrare a prima vista incomprensibile:

#include “Contenuto.h” serve per inserire all’interno della classe Contenitore la classe Contenuto.

Contenuto* c; è la dichiarazione di un puntatore alla classe Contenuto.

Contenitore(Contenuto* q):c(q){}; è il costruttore della classe Contenitore il quale usa il costruttore della classe Contenuto a mezzo del puntatore precedentemente dichiarato.

Contenitore::fcontenitore() è una funzione appartenente alla classe Contenitore.

c -> fcontenuto(); permette di utilizzare la funzione membro fcontenuto() di Contenuto all’interno della classe Contenitore.

Una volta effettuato ciò l’utente della classe Contenitore deve:

  1. creare l’oggetto contenuto
  2. definire e inizializzare un puntatore ad esso
  3. costruire l’oggetto contenitore passandogli il puntatore al contenuto
Ovvero:
main()
{
    Contenuto c(lista valori iniziali);
    Contenuto* ptr=&Contenuto;
    Contenitore x(ptr);
    x.fcontenitore(); // tale chiamata opera su un oggetto
    // contenitore e indirettamente sul
    // contenuto.
}
Effettuiamo ora la traduzione di un generico contenimento stretto avente il seguente diagramma:
La traduzione di tale schema risulta molto più semplice in quanto dovremo esclusivamente:
  • aggiungere una variabile membro alla classe Contenitore di tipo Contenuto
  • implementare il costruttore del Contenuto in modo da richiamare il costruttore del Contenitore.
Associazione fra classi

Per realizzare l’associazione abbiamo bisogno di un metodo che colleghi entrambe le classi mediante l’uso dei puntatori, in quanto non ci basta utilizzare il costruttore di una classe poichè non funzionerebbe nel caso in cui l’associazione fosse binaria.

All’utente toccherà quindi:

  1. creare le due istanze degli oggetti
  2. Collegare le due classi con il metodo apposito passandogli i puntatori
Associazione uno a uno

Proviamo a tradurre il seguente schema UML avente il seguente diagramma:

Esso rappresenta una associazione bidirezionale uno a uno, il che significa che i ruoli dell’associazione sono differenti a seconda della classe che stiamo usando. Supponendo di avere le seguenti specifiche per le classi X ed Y, sarà semplice effettuare l’associazione:

Classe X:

class X
{
    public:
        // lista di inizializzazione per Y
        linker(Y* ptr): ruolo_Y(ptr){} 
    private:
        Y* ruolo_Y;
};
Classe Y:
class Y
{
    public:
        //lista di inizializzazione per X
        linker(X* ptr): ruolo_X(ptr){}
    private:
        X* ruolo_X;
};
Programma utente:
void main()
{
    X x; // crea oggetto X
    Y y; // crea oggetto Y
    // collega i due oggetti
    x.linker(&y);
    y.linker(&x);
}
Associazione uno a molti

Tale tipo di associazione non differisce da quello precedente, l’unica differenza è che dal lato ove compare 1 come molteplicità, bisogna gestire una lista di puntatori all’altro tipo di oggetto, siccome la lista di puntatori non è un argomento trattato nelle nostre guide non faremo un esempio su ciò.

Pubblicità
Articolo precedente