Newsgroup: it.comp.lang.c++
Titolo: Ereditarietà parziale

Salve,
secondo voi potrebbe essere utile una cosa simile:
Indico con "extends" l'ereditarietà "estesa" e con "inherit" l'istruzione
per la ereditarietà parziale.
Pseudo Codice C++ (considerate come public il livello d'accesso di default)
class A {
 int f();
 int x;
 void g();
 char y;
}
class B : A {
 inherit { f(); x} // Se B derivasse da più classi base allora si dovrebbe
// anteporre "ClasseBase::" al membro o funzione ereditata
}
class C extends B {}
A a;
B b;
C c;
int main () {
 a.f(); // ok
 a.x = 1; // ok
 a.g(); // ok
 a.y = 'a'; // ok
 b.f(); // ok
 b.x = 1; // ok
 b.g(); // Errore, g() non esiste in B
 b.y = 'a'; // Errore, y non esiste in B
 c.f(); // ok
 c.x = 1; // ok
 c.g(); // ok, con l'ereditarietà estesa, tramite B eredita tutto quanto è
in A
 c.y = 'a'; // Idem
}
Forse in C++ esiste qualche stratagemma per fare ciò. In ogni caso, pensate
possa essere utile implementare qualcosa di simile?
Grazie
--
Noixe
Posted from X-Privat Free NNTP server - 
www.x-privat.org


Mi è venuta una domanda al 'volo', giusto per spirito di curiosità: ma 
se io ho una classe A con determinate variabili membro / metodi, se non 
mi serve quantomeno RIDEFINIRE qualche metodo oppure ESTENDERE quanto la 
classe base già mi mette a disposizione che senso ha ereditare da essa ?
Alla peggio potresti far diventare privato ciò che non ti serve...

 classe base già mi mette a disposizione che senso ha ereditare da essa ?
Ma in questo caso la classe C non possiede niente per brevita! Era solo per
fare vedere come da una classe B che aveva ereditato da A parzialmente, si
potesse ereditare integralmente.
Il punto è questo:
A è la classe base
B eredita solo quello che gli serve e se vuole aggiunge qualcosa
C si può comportare in uno dei seguenti modi:
1. Eredita parzialmente da B quello che gli serve
2. Eredita tutto il contenuto di A e di B. In questo caso basta fare:
class C extends B
e C contiene tutto quello che è in A e tutto quello che è in B, compreso
quello che era soltanto in A e che B non aveva ereditato prima.
Ciao


 2. Eredita tutto il contenuto di A e di B. In questo caso basta fare:
Ovviamente può anche ereditare da B integralmente semplicemente con
class C : B


Noixe wrote:
[ cut ]
Il problema credo sia concettuale: l'ereditarietà nasce ALMENO per 
ridefinire, in  generale per *ESTENDERE* le funzionalità di una classe.
Adesso sono consapevole del fatto che *MAGARI* se devi ereditare poche 
'feature' da una classe IMMENSA può rivelarsi un problema da un punto di 
vista di OO, ma forse non sarebbe il caso (in quel contesto) di cambiare 
la classe base ?
Cioè se te hai una libreria (ad esempio che ti da della grafica in 
formato testuale - hai presente le curses ? un esempio del genere ) 
nella quale c'è una classe che contiene una classe grande da cui devi 
ereditare poche feature *forse* hai scelto la classe base sbagliata 
oppure una libreria un pò 'fully features' e magari andrebbe bene 
qualcosa di più light..
Ho capito quale è la problematica che esponi, ma la soluzione è nel 
progetto di una classe del genere secondo me, non nell'implementazione.
Non so se hai notato che i miei esempi partono tutti dal fatto che devi 
ereditare da una classe *NON* scritta da te, perchè se una classe del 
genere la scrivi tu ed hai questo problema *forse* la progettazione è 
fatta male...

 qualcosa di più light..
mi correggo, ho fatto un casino : ;)
-> nella quale c'è una classe grande da cui devi
-> ereditare poche feature *forse* hai scelto la classe base sbagliata

 fatta male...
Anche se è stata scritta da altri può darsi che ti serva ereditare solo
alcune caratteristiche dalla classe base. Quindi perchè distingui questo
caso come se fosse diverso da quando sei tu ad implementare la classe base?


 
Perchè una libreria 'grande' come quella di cui stiamo parlando e 
scritta da *te*, seguita dalla necessità di ereditarne *solo* una parte, 
diventa automaticamente una classe progettata male. Perchè *non* 
sviluppare in maniera stratificata ?
E' il classico esempio di cui parla Bertrand Meyer in "Object-Oriented 
Software construction", chiamato "Featurism", ovvero l'ossessione di 
mettere *TUTTE* le feature in un prodotto (adesso ipotizziamo di parlare 
di una classe) indipendentemente dall'effettiva utilità.
Una buona progettazione della famosa classe base ti permetterebbe di 
ereditare da un livello superiore e quindi di 'catturare' un'insieme più 
piccolo di feature e di estendere/aggiungere tranquillamente quello che 
ti serve; in pratica la classe A in una buona progettazione si dovrebbe 
presentare a sua volta come derivata da altre classe, come ultimo 
elemento (dal tuo punto di vista) di una catena di ereditarietà 
all'interno della quale tu puoi tranquillamente trovare il punto più 
consono da cui ereditare per ottenere la classe che ti serve.
j0k3r

 consono da cui ereditare per ottenere la classe che ti serve.
Ciò non toglie però che la possibilità di un'ereditarietà parziale possa
permettere di personalizzare la classe meglio.



E no, è proprio quello il punto. Non si risolve un problema di design 
aggiungendo strumenti al linguaggio.
Vediamo la cosa dal lato opposto: perchè allora un meccanismo del genere 
*non* e' presente nel linguaggio ?

 *non* e' presente nel linguaggio ?
E che ne so? Probabilmente il vantaggio che se ne trarrebbe è minimo
rispetto al lavoro di implementazione?


 pensate possa essere utile implementare qualcosa di simile?
In C++ le interfacce devono essere disegnate e pensate *prima* della
loro dichiarazione, le interfacce costituiscono i contratti di
collaborazione fra le varie entita' del sistema. Il compilatore ti
assicura a compile-time il rispetto di questi contratti. 
La semantica dell'ereditarieta' (pubblica) in C++ e' piuttosto forte,
se una classe B eredita da una classe A, ogni oggetto istanziato di B
e' anche istanza di A [**], il compilatore per assicurare questo
fatto obbliga l'interfaccia della classe B ad essere un superset
dell'interfaccia della classe A (eventualmente puo' modificare
l'implementazione di alcuni metodi => overriding, ma conservandone la
signature => conservando la stessa interfaccia). 
[*] Gli attributi sono un altro discorso, almeno che tu non abbia
setter/getter non e' possibile fare il trucchetto di cui sopra
[**] Anche citato come principio di sostituibilita' di Liskov, ma in
realta' e' la relazione semantica di sottotipo
-- 
{}-{}-{}-{}-{}-{}-{}-{}-{}-{}-{}-{}-{}-{}-{}-{}-{}-{}-{}
SunGray aka Gabriele Lana
contact me @ 
gabrie...@fastwebnet.it
See you space cowboy ...

 dell'interfaccia della classe A
Non era un subset? O mi sfugge qualcosa?
EMG
--
EMAG Software Homepage:     
http://www.emagsoftware.it
The EMG's ZX-Spectrum Page: 
http://www.emagsoftware.it/spectrum
The Best of Spectrum Games: 
http://www.emagsoftware.it/tbosg

 EMG


 Non era un subset? O mi sfugge qualcosa?
Se B eredita da A, B avrà un numero di metodi e data member maggiore o
uguale a quelli di A. Quindi, da un punto di vista insiemistico, l'insieme
delle intefacce di B contiene intefacce di A.
Ciao


 questa e' filosofia)
Si! Filosofiamo!
Dato un insieme R di richieste e un insieme O di oggetti che soddisfano le
richieste di R, esisterà sempre un limite L tale che se superato dal numero
di richieste di R (cardinalità di R) farà aumentare anche la cardinalità di
O (ovvero gli oggetti necessari a soddisfare le richieste). Possiamo anche
definire una multifunzione f : R -> P(O) definita nell'insieme delle
richieste a valori nell'insieme delle parti dell'insieme degli oggetti che
soddisfano le richieste di R.
Chiaramente, se esiste un x appartenente a R : f(x) non appartiene a P(O)
allora il codominio dovrà essere ingrandito proprio per soddisfare la
richiesta x. Quindi aggiungeremo un oggetto y a O tale da soddisfare la
richiesta x, ma in questo modo aumentiamo la cardinalità di O e ovviamente
anche la cardinalità di P(O).
Va bè, la finisco quì :)
Ciao


 delle intefacce di B contiene intefacce di A.
Sì, che non ci sia niente nel linguaggio che impedisca l'estensione
dell'interfaccia è chiaro ma quello che mi chiedo è cosa farne dei
metodi in più in un contesto di sostituibilità. Probabilmente ho solo le
idee un po' confuse...


 sottoinsieme dell'insieme delle istanze di tipo "classe base".
Ma queste due affermazioni non sono in contraddizione? Facciamola più
semplice: ho una classe A con dieci metodi. Una classe B deriva da A e
quindi ha almeno quei dieci metodi. Se ne aggiungo altri nella sola
classe B, i nuovi non potranno essere utilizzati dove è richiesto un
tipo A. Dove sbaglio?


tipo A. Dove sbaglio?
E' stato fatto un po' di casino.
In parole povere:
La classe derivata e' un superset della classe base, perche' la
implementa tutta la base piu' le estensioni.
-- 
ciao, hal

 tipo A. Dove sbaglio?
Credo che il modo giusto per capire questo è pensare che un'istanza di B è
anche un'istanza di A, ma offre di più. Quindi, data una richiesta essa può
essere soddisfatta da:
1. Un'istanza di A
2. Un'istanza di B (che in fondo è anche di A)
Non accadrà mai che ci sia un'istanza di B che offra solo i servizi
aggiuntivi che ha B rispetto ad A.
A meno che non si implementi l'ereditarietà parziale da me esposta nel primo
messaggio :P
In quel caso se B eredita da A parzialmente, allora potrebbero esiste
istanze di B che sono anche istanze di A ma solo parzialmente :-D
Ciao


 richiesto un tipo A. Dove sbaglio?
Non sbagli, supponiamo
A classe base
B classe derivata
Si sta parlando di due insiemi diversi:
- L'insieme dei servizi fornito da una classe: in questo caso
l'insieme dei servizi di A (che ha dieci metodi) e' un sottoinsieme
dei servizi di B (che ha almeno i dieci metodi derivati)
- L'insieme di tutti gli oggetti istanza di una classe: visto che per
il principio di sostituibilita' (forzato dal linguaggio) tutte le
istanze di B sono anche istanze di A, allora e' corretto dire che
l'insieme delle istanze di A e' un superset delle istanze di B, in
quanto contiene tutte le istanze di A e anche quelle di B (ed in
generale di tutte le sue derivate),


 implementa tutta la base piu' le estensioni.
Però, se ho capito bene, perché funzioni la sostituibilità non devono
essere presenti estensioni nella classe derivata, dico bene?


 anche un'istanza di A, ma offre di più.
E' qui che non mi torna. E' vero che il linguaggio non ti impedisce di
aggiungere servizi alla classe derivata ma questo non rompe il principio
di sostituibilità? No, forse ho capito: il fatto che la classe derivata
abbia dei servizi in più significa soltanto che non verranno utilizzati
quando la classe derivata è considerata classe base (cioè quando
l'istanza di classe derivata viene passata ad una funzione che richiede
un tipo base) ma questo non significa che non funzionerebbe ugualmente.
Ci sono?


 generale di tutte le sue derivate),
Credo di aver capito, grazie.


 Ci sono?
Direi di si, se ho ben capito cosa intendeva SunGray. Infatti io ho scritto
che l'istanza di B è *anche* un'istanza di A e quindi intendevo dire che in
alcuni casi, all'istanza di B non si richiede di invocare servizi aggiuntivi
di B ma solo quelli ereditati da A.


 di B ma solo quelli ereditati da A.
Grazie.
