Newsgroup: it.comp.lang.c++
Titolo: Quale è una possibile implementazione elegante del seguente problema?

Quale è, secondo voi, una possibile implementazione elegante del seguente
problema?
Si considerino due classi, rispettivamente Soggetto e Indirizzo, fra le
quali esiste un'associazione bidirezionale di tipo 0..1 <-> 0..n . Nel
mondo reale ciò potrebbe rappresentare l'associazione che esiste fra una
persona e gli indirizzi delle abitazioni di cui può o può non essere
correntemente proprietario.
La classe Soggetto dovrà esportare solo due metodi pubblici,
aggiungiIndirizzo e rimuoviIndirizzo, che prendono come parametro un
puntatore a un oggetto di tipo Indirizzo ed eseguono le operazioni omonime
sulla lista di indirizzi gestita internamente.
La classe Indirizzo dovrà esportare un solo metodo pubblico impostaSoggetto,
che prende come parametro un puntatore a un oggetto di tipo Soggetto ed
associa l'indirizzo al più ad un soggetto (cioè, in particolare, se
l'argomento è null, l'indirizzo viene rimosso dal soggetto a cui
l'indirizzo stesso è eventualmente associato).
In definitiva, con i tre metodi suddetti dovrà essere possibile:
1. aggiungere un indirizzo ad un soggetto:
   per esempio, s1->aggiungiIndirizzo(i);
2. rimuovere un indirizzo ad un soggetto:
   per esempio, s1->rimuoviIndirizzo(i);
3. assegnare un (altro) soggetto ad un indirizzo:
   per esempio, i->impostaSoggetto(s2)
4. rimuovere un eventuale soggetto ad un indirizzo:
   i->impostaSoggetto(null).
In virtù della cardinalità dell'associazione, si tenga ben presente che:
1. tramite Soggetto::aggiungiIndirizzo(), prima di aggiungere l'indirizzo al
nuovo soggetto, si dovrà prevedere in qualche modo anche la rimozione del
medesimo indirizzo dal soggetto che eventualmente lo detiene in quel
momento.
2. analogamente, tramite Indirizzo::impostaSoggetto(), prima di assegnare il
nuovo soggetto all'indirizzo, si dovrà prevedere anche la rimozione
dell'indirizzo dal soggetto che eventualmente lo detiene in quel momento.
Personlamente, per quanto possa essere invitante sfruttare reciprocamente le
interfaccie esposte dalle due classi per aggiornare gli oggetti
vicendevolmente, non riesco a trovare una soluzione non ricorsiva che al
tempo stesso superi tutte le regole di "buona programmazione".
Resto in attesa di vostre idee a proposito. Avete carta bianca sulla
rappresentazione interna o privata delle classi.
Grazie.

 tempo stesso superi tutte le regole di "buona programmazione".
Mi pare che questa presentazione sia accademica. Per come hai presentato 
le cose è sufficiente che ogni Indirizzo abbia un puntatore ad un 
Soggetto, e i metodi che hai descritto hanno un'implementazione banale. 
In pratica però tu vorrai poter determinare l'Indirizzo di un Soggetto e 
quindi la classe Soggetto dovrà avere al suo interno una lista di 
puntatori a Indirizzi. Anche in questo caso l'implementazione è banale.
Soluzioni "ricorsive" non esistono in questa situazione... non vedo come 
tu la possa trovare "invitante".
E.


Sarei curioso di vedere la tua implementazione :) Ricorda che le
classi devono essere abbastanza intelligenti da garantire in ogni
istante, dopo ciascuna delle tre operazioni, la cardinalità
dell'associazione, come ho detto: a ogni soggetto possono essere
associati zero o piu' indirizzi e ad ogni indirizzo può essere
associato al piu' un soggetto.
Se, per esempio, nel flusso del programma si aggiunge lo stesso
indirizzo a due soggetti diversi:
soggetto1->aggiungi(indirizzo1);
soggetto2->aggiungi(indirizzo1);
bisognerà garantire la rimozione di indirizzo1 da soggetto1 prima di
assegnarlo a soggetto2.
Analogamente, prima di assegnare un (unico) nuovo soggetto ad un
indirizzo:
indirizzo1->imposta(NuovoSoggetto);
bisognerà prima rimuovere l'indirizzo da un eventuale VecchioSoggetto
a cui è assegnato e solo poi aggiungerlo al NuovoSoggetto.
Spero di essere stato chiaro.

 Sarei curioso di vedere la tua implementazione :) 
// Qualcosa del genere (non testato):
#include <list>
class Soggetto {
private:
    friend class Indirizzo;
    std::list<Indirizzo*> indirizzi;
public:
    void aggiungiIndirizzo(Indirizzo *i);
    void rimuoviIndirizzo(Indirizzo *i);
};
class Indirizzo {
private:
   Soggetto *soggetto;
public:
    Indirizzo(): soggetto(0) {}
    void impostaSoggetto(Soggetto *s) {
       if (soggetto)
          soggetto->indirizzi.erase(soggetto->indirizzi.find(this);
       soggetto=s;
       if (soggetto)
          soggetto->indirizzi.push_back(this);
    }
};
void Soggetto::aggiungiIndirizzo(Indirizzo *i) {
       i->impostaSoggetto(this);
    }
void Soggetto::rimuoviIndirizzo(Indirizzo *i) {
       assert(indirizzi.find(i)!=indirizzi.end());
       i->impostaSoggetto(0);
    }
// E.

 - Mostra testo citato -
Si, cosi' e' come avevo pensato io inizialmente. La cosa che mi da
fastidio e' che la classe Indirizzo dipenda dall'implementazione di
Soggetto. Non esiste a questo punto un modo per nascondere i dettagli
di Soggetto ad Indirizzo?
Grazie

 di Soggetto ad Indirizzo?
Tra i punti da rispettare c'è:
"La classe Indirizzo dovrà esportare un solo metodo pubblico
impostaSoggetto,
che prende come parametro un puntatore a un oggetto di tipo Soggetto ed
associa l'indirizzo al più ad un soggetto (cioè, in particolare, se
l'argomento è null, l'indirizzo viene rimosso dal soggetto a cui
l'indirizzo stesso è eventualmente associato)."
Quindi indirizzo deve poter aver accesso ai dettagli di soggetto.
Se vuoi usare solo due classi, non vedo altri modi se non farle comunicare
direttamente.

 problema?
Mi permetti una nota critica?
Secondo me non c'è una soluzione elegante, per il fatto che i
requisiti stessi sono "sporchi".
Perché la classe Indirizzo dovrebbe conoscere la classe Soggetto?
Non lo trovo né logico, né utile.
A rigor di logica, un indirizzo è un recapito, a prescindere da chi lo
possegga e in ogni caso io attribuirei ad una terza classe di
controllo (per es. Agenda) la gestione delle assegnazioni di cosa a
chi.


L'Agenda va gestita "manualmente". Volevo cercare di ottenere le
stesse informazioni di un'agenda operando su uno solo (a scelta) fra i
due tipi "intellegenti" di oggetti.

 chi.
Concordo con te, tra l'altro la soluzione richiesta dai requisiti (solo 
le 2 classi Soggetto ed Indirizzo) potrebbe causare problemi quando si 
dovrï¿œ modificare il tutto perchï¿œ ci si ï¿œ resi conto che ad un indirizzo 
possono corrispondere piï¿œ soggetti (piï¿œ persone possono abitare nello 
stesso appartamento o piï¿œ attivitï¿œ possono avere la sede legale allo 
stesso indirizzo, ecc..) :)
Ciao

 stesso indirizzo, ecc..) :)
Su questo non sono d'accordo. Penso che il vantaggio di "condividere" 
gli indirizzi sia molto basso, e invece i rischi possano essere alti.
Se uno dei soggetti che condivide un indirizzo cambia casa, rischi di 
cambiare indirizzo anche a tutti gli altri. Anche se il programma ï¿œ 
fatto bene, l'utente potrebbe essere messo in difficoltï¿œ da una simile 
scelta progettuale.
E.

 scelta progettuale.
Non confondiamo le cose. L'associazione è 0..1 <-> 0..n e il programma
deve rispettare tale cardinalità, a prescindere dall'applicazione nel
mondo reale. Quindi non deve e non può sussistere il caso di più
soggetti legati ad uno stesso indirizzo. Io come esempio reale, forse
infelice, ho parlato del legame di "proprietà", non di "abitazione",
che sussiste fra una persona e un generico appartamento (descritto da
un indirizzo). "Infelice" perchè ho escluso il caso di conproprietà,
che in realtà può pur succedere.

 di Soggetto ad Indirizzo?
Infatti io *non* scriverei mai una cosa cosi', ma per gli stessi motivi
di Andrea. Le specifiche fanno schifo. Ma fanno schifo *forte*.
Direi che vengono da un testo di un esercizio dato in universita'.
Sono di livello orribilmente basso e dicono nero su bianco di fare una
cosa *sbagliata*. 
***
Comunque in particolare non capisco perche' non girare le cose. Temo
solo che si violino le specifiche puntate in questo modo. Forse non
dovrei postare in questo stato...
aggiungiIndirizzo aggiunge l'indirizzo passato alla lista e chiama su
questo impostaSoggetto (che si limita a chiamare eventualmente
rimuoviSoggetto sul suo vecchio soggetto). 
rimuoviIndirizzo rimuove dalla lista e impostaSoggetto a 0 [nota la
ricorsione non avviene per i check su 0 e non 0]. 
Con un po' di sbattimenti ma nemmeno troppi si potrebbe probabilmente
renderlo anche exception safe.
Ora quanto detto a partire dai tre asterischi prendetelo con le molle
perche' e' tutta sera che preparo slides per fare vedere un po' di
Python stra-dinamico ad un laboratorio di studenti abituati a linguaggi
statici e sono tendenzialmente cotto.
-- 
-riko

 cosa *sbagliata*.
Non capisco questi aggettivi qualificativi sulle specifiche. Sono
così, che piacciano o meno. Se i termini "Soggetto" e "Indirizzo" nel
caso specifico ti confondono, astrai il problema e affrontalo in
maniera generica: "fra due oggetti del tipo che ti pare c'e' una
associazione 0..1 <-> 0..n". Non vedo perchè presi due di questi
oggetti essi non possano restare sincronizzati in maniera
"intelligente", garantendo la cardinalità richiesta internamente. Ed
inevitabilmente la miglior implementazione in questo caso richiede che
un oggetto conosca i dettagli dell'altro. E, allora, la parola chiave
"friend" che l'hanno inventata a fare?

 "friend" che l'hanno inventata a fare?
Pff..ovviamente volevo dire "fra due classi di oggetti"


La tua risposta mi lascia perplesso...
Francamente non vedo cosa questo c'entri con scelte progettuali o meno.
Non ï¿œ che per evitare difficoltï¿œ all'utente l'applicazione si possa 
comportare in modo errato o non fornire funzionalitï¿œ necessarie.
Ciao.

 soggetti legati ad uno stesso indirizzo.
Sono d'accordo che l'applicazione debba rispettare i requisiti, anche se 
ci sarebbe da discutere su cosa sia necessario fare in caso di requisiti 
errati (non che questo sia il caso, perchï¿œ i dati che ci hai fornito 
sono pochi, anche se dall'esempio che hai fatto si direbbe di sï¿œ), ma ti 
vorrei far presente che molto spesso i requisiti mutano nel tempo 
(dipende molto dalla tipologia di applicativo), e non pensare a questo 
in anticipo potrebbe essere una fonte molto grossa di problemi.
Ciao

 soggetti legati ad uno stesso indirizzo. 
Infatti. E' quello che sto dicendo. E sto dicendo che ritengo giusto che 
se due persone abitano nella stessa casa, gli oggetti indirizzo siano 
comunque due oggetti distinti, anche se con le stesse propriet�.
E.

Sergio ha scritto:

Perchï¿œ dici che si comporta in maniera errata? Quali funzionalitï¿œ non 
fornisce?
Sto dicendo che, a mio parere, l'indirizzo di una persona dovrebbe 
essere tra le proprietï¿œ della persona, come nome, cognome etc. Persone 
diverse non dovrebbero avere un "link" allo stesso indirizzo, cosï¿œ come 
il cognome di Paolo Rossi e quello di Vasco Rossi vengono sempre 
rappresentati in un database come due stringhe indipendenti, e a nessuno 
verrebbe in mente di fare una tabella di cognomi e di mettere a Paolo e 
a Vasco un link allo stesso cognome.
E.

 a Vasco un link allo stesso cognome.
Ok, sono d'accordo, ma solo se Indirizzo non contiene un riferimento a 
Soggetto e l'associazione Indirizzo -> Soggetti ï¿œ mantenuta esternamente.
Se invece Indirizzo contenesse un riferimento a Soggetto non avresti 
modo, dato un indirizzo, di risalire a tutte le persone che abitano a 
quell'indirizzo.
Ciao