C++ – Classe aggregato
Nell’ultimo esempio l’oggetto libro interagiva con una tabella contenente i libri della biblioteca. In pratica la logica di scrittura del programma restava sempre la stessa e i nuovi elementi introdotti (classi ed oggetti) venivano inseriti in quel contesto. Tutto ciò limita fortemente i vantaggi dell’uso di questi nuovi strumenti.
L’esempio proposto aveva principalmente valenza didattica: si trattava di rendere più soft il passaggio ad una nuova concezione di progettazione e sviluppo dei programmi. In realtà il vantaggio maggiore degli oggetti, e la straordinaria diffusione che hanno avuto, è giustificato dal fatto che l’uso di queste nuove tecniche rende più semplice la scrittura dei programmi avvicinandola al nostro modo di risolvere i problemi: il mondo che ci circonda è fatto di oggetti che hanno delle proprie caratteristiche (comportamenti) e che interagiscono tra di loro scambiandosi messaggi (come li chiama la OOP). L’oggetto che riceve il messaggio si comporta in maniera coerente con le azioni legate al messaggio.
Nella vita reale, un dipendente ha dei compiti specifici da assolvere e che mette in atto in conseguenza a direttive (i messaggi) che riceve. Se poi il dipendente è inserito in un sistema complesso (per esempio è inserito in un contesto produttivo), interagisce con altri dipendenti che hanno, anche loro, compiti specifici. In pratica per la risoluzione di un problema bisogna pensare di far lavorare insieme oggetti che interagiscono fra di loro scambiandosi messaggi: è quello che viene chiamato paradigma della programmazione ad oggetti.
“Il paradigma della programmazione ad oggetti è il seguente: si determini quali classi si desiderano; si fornisca un insieme completo delle operazioni di ogni classe; si renda esplicito ciò che hanno in comune con l’eredità” (B.Stroustrup)
Si cercherà di adottare un simile approccio alla risoluzione del problema affrontato nell’esempio proposto della gestione dei prestiti di una biblioteca (dell’ereditarietà degli oggetti se ne parlerà più avanti).
Si era già definita la classe libro (la definizione è contenuta nel file c_libro), ora sarà necessario definire la classe libreria (la cui definizione, per esempio, sarà registrata nel file c_libreria): classe aggregato di oggetti di tipo libro.
Anche per la libreria si possono fare osservazioni simili a quelle fatte allorquando si è trattato di definire la classe libro. Una libreria è non solo un contenitore di libri ma anche un gestore degli stessi (non può essere solo una specie di buco nero dove far scomparire i libri). Deve poter permettere operazioni minime di manutenzione: aggiungere un nuovo libro, aggiornare i dati registrati, estrarre i dati di un libro, fornire informazioni sulla quantità di libri esistenti. Si potrebbero aggiungere anche tante altre funzioni che deve assolvere una libreria (per esempio eliminazione di un libro non più presente) ma, nell’esempio proposto, ci si limiterà per semplicità a implementare nella classe solo i metodi descritti. D’altra parte è importante ricordare l’osservazione che in questo modo non si toglie generalità: esiste infatti nella programmazione ad oggetti, come d’altra parte si è già avuto modo di evidenziare, una proprietà degli oggetti (l’ereditarietà) che permette di ampliare le funzionalità di una classe in maniera semplice ed efficiente e, se necessario, modificare le funzionalità esistenti.
#ifndef C_LIBRERIA /*1*/
#define C_LIBRERIA /*1*/
#include <vector>
using namespace std;
#include "c_libro" /*2*/
namespace biblioteca{
class libreria{
public:
int aggiungi(libro); /*3*/
bool estrai(int, libro&) const; /*3*/
bool aggiorna(int, libro); /*3*/
int dotazione() const {return bib.size();}; /*3*/
protected:
vector<libro> bib; /*4*/
};
// Aggiunge un libro
// ritorna la posizione di inserimento
int libreria::aggiungi(libro lib) /*5*/
{
bib.push_back(lib);
return dotazione()-1;
};
// Estrae le informazioni su un libro
// ritorna valore logico sul successo dell'operazione
bool libreria::estrai(int quale,libro& lib) const /*6*/
{
bool estrattoOK=false;
if(quale>=0 && quale<dotazione()){ /*7*/
lib = bib.at(quale);
estrattoOK=true;
}
return estrattoOK;
};
// Aggiorna i dati di un libro esistente
// ritorna valore logico sul successo dell'operazione
bool libreria::aggiorna(int quale,libro lib) /*8*/
{
bool eseguito=false;
if(quale>=0 && quale<dotazione()){
bib.at(quale)=lib; /*9*/
eseguito = true;
}
return eseguito;
};
}
#endif /*1*/
Le tre righe 1, due all’inizio del file di definizione della classe, una alla fine, sono direttive al pre-compilatore. Capiterà spesso, da ora in poi, di avere necessità di includere più volte, in più file di codice, le classi che servono. Nell’esempio proposto il file c_libro viene incluso nella definizione della nuova classe, che è appunto un aggregato di libri, ma anche nel programma di gestione della biblioteca che necessita di definire oggetti di quella classe. Il compilatore si troverebbe, in questo caso, una duplicazione di definizioni e non potrebbe assolvere alla propria funzione. Per evitare il rischio di duplicati, nei file di intestazioni delle classi, si aggiungono direttive che dicono al compilatore che se non è definita una certa variabile, nel caso in esame C_LIBRERIA, allora si definisce la variabile (seconda riga) e si prosegue. Se invece la variabile esiste (il file è già stato incluso), si passa alla fine della condizione (ultima riga), evitando una definizione duplicata.
Anche nel file di definizione della classe libro si dovranno aggiungere righe simili, per esempio, per definire la variabile C_LIBRO.
L’inclusione della 2 è qui necessaria per poter definire oggetti della classe libro.
I metodi pubblici della classe, definiti in 3, permettono le operazioni minime che servono per la gestione dei prestiti per come è stata enunciata. L’aggregato di libri (la libreria) è definito nella 4 come vettore di tipo libro, ma di questo chi usa la classe, non ha alcuna percezione: esistono solo i metodi pubblici per gestire gli oggetti della classe. I metodi estrai e dotazione non modificano i dati della classe.
Il metodo aggiungi della 5 inserisce un nuovo libro nella libreria e restituisce, qualora serva, la posizione in cui è stato inserito.
Il metodo estrai, definito in 6, restituisce le informazioni su un libro di cui viene specificata la posizione. Se la posizione è fuori dall’intervallo permesso (controllo in 7), restituisce un valore booleano false.
il metodo aggiorna della 8 mette un libro, ricevuto come parametro, nella libreria in una posizione che è ricevuta anch’essa come parametro. L’assegnazione alla posizione è effettuata dalla 9. Il metodo restituisce false se l’aggiornamento non è stato possibile (posizione fuori dai limiti ammessi).
Indice corso: C++ Appunti di Programmazione Fonte: prof. Nunzio Brugaletta
