Quando un progetto diviene troppo complesso allora, per poter essere gestito,
é necessario modularizzarlo ovvero:
- il progetto viene strutturato in parti separate
- si stabiliscono relazioni precise tra le parti
La modularizzazione è quindi la suddivisione in parti (moduli) di un progetto, in modo che
sia più semplice da comprendere e manipolare.
Questo processo richiede che ciascuna parte del progetto realizzi un particolare aspetto o un comportamento all’interno dell'intero sistema.
Occorre quindi effettuare un processo di astrazione.
L'astrazione porta ad individuare e considerare le proprietà rilevanti di un’entità, ignorando i dettagli non essenziali.
Le proprietà prese in considerazione definiscono una particolare "vista" dell’entità. Ad esempio una entità "Persona" può essere analizzata dal
punto di vista "anagrafico" o "clinico".
vista "anagrafica": Nome, cognome, data di nascita, luogo di nascita, residenza ...I meccanismi di astrazione più diffusi sono:
vista "clinica": Temperatura corporea, peso, pressione arteriosa, ...
Esempio di modularizzazione
Immaginiamo di voler implementare un modulo che realizzi alcune funzioni nell'ambito dei numeri complessi: ad esempio la stampa e la lettura di un numero complesso, la somma e la differenza tra due numeri complessi. Seguendo le osservazioni fatte la modularizzazione del nostro progetto potrebbe essere la seguente:Main.c | Complex.h | Complex.c | ||
---|---|---|---|---|
#include <stdio.h> #include "complex.h" int main() { tComplex a,b,s,d; // INPUT a=Leggi(); b=Leggi(); // ALGORITMO s=Somma(a,b); d=Differenza(a,b); // OUTPUT Stampa("Somma = ",s); Stampa("Differenza = ",d); fflush(stdin); getchar(); return(0); } |
#ifndef COMPLEX_H #define COMPLEX_H // Strutture dati typedef struct { float r; /* parte reale */ float i; /* parte immaginaria */ } tComplex; // Prototipi tComplex Leggi(); tComplex Differenza(tComplex, tComplex); tComplex Somma(tComplex, tComplex); void Stampa(char *, tComplex); #endif |
#include <stdio.h> #include "complex.h" tComplex Leggi() { tComplex x; printf("Digita un complesso (a+bi):"); scanf("%f%fi", &x.r, &x.i); return(x); } tComplex Somma(tComplex a, tComplex b) { tComplex x; x.r=a.r+b.r; x.i=a.i+b.i; return(x); } tComplex Differenza(tComplex a, tComplex b) { tComplex x; x.r=a.r-b.r; x.i=a.i-b.i; return(x); } void Stampa(char *p, tComplex a) { printf("%s",p); if (a.i==0) printf("%.2f",a.r); else if (a.i>0) printf("%.2f+%.2fi\n",a.r,a.i); else if (a.i<0) printf("%.2f-%.2fi\n",a.r,-a.i); } |
Quando più programmatori lavorano simultaneamente ad un progetto di grandi
dimensioni, una volta accordatisi sulla specifica (interfaccia) dei vari moduli,
possono procedere all’implementazione dei rispettivi moduli indipendentemente
l’uno dagli altri.
Creare un progetto
Vediamo come costruire l’applicazione COMPLEX utilizzando l’ambiente DEV_C++ attraverso la costruzione di un progetto. Si opera nel modo seguente:
- Ripetiamo l'operazione in modo da ottenere i files necessari. A questo punto
l’applicazione è costituita da tre file e due moduli (main e complex), si può
iniziare a scrivere il codice. Al termine dovremo avere questa situazione:
Organizzazione dei moduli
L’organizzazione in moduli di un sistema software è un problema trattato dall’ingegneria del software.
E’ possibile organizzare in moduli secondo diverse metodologie di sviluppo:
- Top down: si parte dall’alto, considerando il problema nella sua interezza e si procede verso il basso per raffinamenti successivi fino a ridurlo ad un insieme di sottoproblemi elementari.
- Bottom up: si risolvono singole parti del problema, senza averne necessariamente una visione d’insieme, per poi risalire procedendo per aggiustamenti successivi fino ad ottenere la soluzione globale.
Nella modularizzazione possono essere adottati diversi criteri che tengano conto di alcuni elementi:
Information hiding -
Coesione -
Accoppiamento.
INFORMATION HIDING
Consiste nel nascondere e proteggere (incapsulare) alcune informazioni di una entità all’interno del modulo
stesso. L’accesso alle informazioni protette è fornito in maniera controllata solo attraverso l’interfaccia.
Esempio di Information hiding:
Supponiamo che un modulo realizzi l’astrazione "Persona" fornendo una
struttura dati di tipo
"Persona".
Una persona, nel livello di astrazione scelto, è caratterizzato da: Codice Fiscale, Nome, Cognome, Data e Luogo di nascita, Residenza,
...
Il modulo esporta attraverso la sua interfaccia il tipo "persona" ed
solo alcune operazionicon le quali è possibile effettuare delle modifiche sulle variabili
"nascoste" di quel
modulo: ad esempio inizializzazione dei dati della persona, stampa dei dati etc…
Non è possibile per i "moduli clienti" del modulo modificare o leggere i dati di una variabile persona se non utilizzando le funzionalità esportate da
tale modulo.
Nella definizione dei criteri relativi all'Information hiding valutare con attenzione cosa esportare e cosa nascondere.
Le interfacce devono essere:
- minimali, cioè devono esporre solo ciò che è strettamente necessario;
- stabili, cioè possibilmente non devono subire cambiamenti nel tempo:
Se l’interfaccia non cambia, le informazioni nascoste possono essere modificate senza che questo influisca sulle altre parti del sistema di cui l’entità fa parte.
ACCOPPIAMENTO
Due moduli si dicono debolmente accoppiati se le dipendenze tra di essi sono minimizzate.
In altre parole un modulo risulta debolmente accoppiato con il "modulo utente" se può essere usato con altri
"moduli utente" senza apportate alcuna
modifica allo stesso. Per minimizzare l'accoppiamento occorre limitare al
massimo l’uso di variabili globali (visibili e utilizzabili da più moduli) poiché
queste creano dipendenze non facilmente controllabili.
COESIONE
Un modulo è fortemente coeso se incapsula un insieme di caratteristiche omogenee, sufficientemente indipendenti da altri moduli.
Un esempio di modulo fortemente coeso è un modulo che realizza un’ unica astrazione
{Persona, Ordinamento} (l'implementazione relativa all'ordinamento è
legata all'entità Persona e non può essere applicato ad altre entità).
Nello sviluppo software la Coesione e l'Accoppiamento suggeriscono criteri contrastanti, tra i quali è opportuno valutare un punto di equilibrio (tradeoff).
L’ideale sarebbe riuscire ad ottenere una alta coesione tra i moduli ed un basso accoppiamento, ma:
- un livello molto alto di coesione dei moduli genera in generale una eccessiva crescita delle dipendenze tra di essi.
- un livello molto basso di accoppiamento può determinare uno scadere della qualità delle astrazioni realizzate dai moduli.
Link utili
http://www.math.unipd.it/~sperduti/CORSO-C%2B%2B/Strutture.htmm