Confusione sull'ereditarietà

2

So che potrei avere downvoted per questo, ma sono davvero curioso.

Mi è stato insegnato che l'ereditarietà è uno strumento di polimorfismo molto potente, ma non riesco a usarlo bene in casi reali.

Finora, posso usare l'ereditarietà solo quando la classe base è una classe astratta.

Esempi:

  1. Se parliamo di Product e Inventory , ho rapidamente pensato che un Product sia un Inventory perché anche Product deve essere inventato . Ma si è verificato un problema quando l'utente voleva vendere il suo articolo Inventory . Non sembra giusto modificare un oggetto Inventory nel suo sottotipo ( Product ), è quasi come provare a convertire un genitore nel suo figlio.

  2. Un altro caso è Customer e Member . È logico (almeno per me) pensare che un Member sia un Customer con altri privilegi. Lo stesso problema si è verificato quando l'utente desiderava aggiornare un Customer esistente per diventare Member .

  3. Un caso molto banale è il caso Employee . Dove Manager , Clerk , ecc. Possono essere derivati da Employee . Ancora, lo stesso problema di aggiornamento.

Ho provato a utilizzare la composizione per alcuni casi, ma volevo davvero sapere se mi manca qualcosa per la soluzione di ereditarietà.

La mia soluzione di composizione per questi casi:

  1. Crea un riferimento di Inventory in Product . Qui sto facendo un'ipotesi sul fatto che Product e Inventory stiano parlando in un contesto diverso. Mentre Product è nel contesto delle vendite (prezzo, volume, sconto, ecc.), Inventory è nel contesto della gestione fisica (stock, movimento, ecc.)

  2. Crea un riferimento di Membership invece che all'interno di Customer class invece della precedente soluzione di ereditarietà. Pertanto, l'aggiornamento di un Customer consiste solo nell'istanziare la proprietà Customer di Membership .

  3. Questo esempio continua a essere insegnato nelle classi di programmazione di base, ma penso che sia più corretto avere quei Manager , Clerk , ecc derivati da una classe Role astratta e renderli una proprietà in Employee .

Ho trovato difficile trovare un esempio di classe concreta derivante da un'altra classe concreta.

Esiste una soluzione di ereditarietà in cui posso risolvere questi casi?

Essendo nuovo in questa cosa OOP, ho davvero bisogno di una guida.

Grazie!

    
posta Samuel Adam 11.11.2013 - 05:31
fonte

6 risposte

11

La prima cosa che suggerirò è che ti prendi qualche minuto per leggere alcune delle altre domande e risposte sull'ereditarietà e composizione che sono qui su Stack Exchange. Ci sono diversi collegamenti sulla barra laterale, per esempio.

Mi occuperò solo del tuo primo esempio:

Inventory non dovrebbe ereditare da Product. (stai vendendo un an inventario? Come posso portarlo a casa?)

Né dovrebbe Product ereditare da Inventory. Come è un vendibile oggetto, come un laptop, un elenco di oggetti in vendita?

Anche la tua soluzione di composizione per questo sembra un po 'arretrata. Penserei a un Inventory come a informazioni su (Contiene riferimenti a) molti diversi Product s. Potrebbe anche contenere informazioni che lo stesso Product non conosce. (Quanti% diXYZ_Laptop_Product s sono disponibili? Quanti ne riceviamo la prossima settimana?)

BTW, per un esempio concreto, XYZ_Laptop_Product potrebbe ereditare da Electronic_Product , che potrebbe ereditare da Product . Inoltre, potresti avere solo una classe Product e l'unico modo in cui un laptop differisce da un paio di calze sono i campi del numero di magazzino, del prezzo e della descrizione.

    
risposta data 11.11.2013 - 07:35
fonte
3

Forse sarebbe d'aiuto se implementassimo uno di questi casi in un caso di codifica dell'eredità.

Espandiamoci sull'elemento 2.

Il cliente è un oggetto di base. Diciamo che ha le seguenti proprietà:

Customer { 
customer_number, 
debt, 
name, 
age. 
}

Creazione di un cliente (Costruttore):

Customer(customer_number, debt, name, age){...}

Può comprare:

//Purchase item means adding to the customer's debt to the store.
buy(Object o)
{
    debt = debt + o.price;
} 

Ritorno:

//Return item means reducing the customer's debt (adding credit), minus a 10% restocking fee.
return(Object o)
{
    debt = debt - o.price * 0.9;
}

Può fare cose come, stampare nome, memorizzare debito / credito ecc.

double get_debt()
{
    return debt;
}

Ha un minimo ingombro di memoria come oggetto in quanto contiene solo quattro proprietà inizializzate e la funzione di livello base.

Ma ora vogliamo espandere la classe Customer in una classe Member, ha nuovi vantaggi nell'essere un membro, hai dei punti status. E non devi pagare una tariffa di riassortimento. Quindi ogni volta che acquisti qualcosa come oggetto Membro, questo viene automaticamente funzionato.

Member extends Customer {
    status_points;
}

Vorresti la possibilità di creare un membro da zero o da un cliente esistente. Super chiama il metodo / costruttore del genitore.

Member(customer_number, debt, name, age, new_status_points) {
    super(customer_number, debt, name, age);
    status_points = new_status_points);
}

Member(Customer a, new_status_points){
    super(a.customer_name, a.debt, a.name, a.age);
    status_points = new_status_points;
}

La funzione di acquisto del membro cambia (super chiama la classe genitore):

@Override
buy(Object o)
{
    super.buy(o);
    status_points = status_points + o.price / 10;
}

Funzione di restituzione del membro con il bonus del membro di nessuna commissione di ritorno.

@Override
return(Object o)
{
   debt = debt - o.price;
   status_points = status_points - o.price / 10;
}

Il membro ha anche una funzione di aggiunta, che stampa il loro livello di stato.

print_status()
{
    if(status_level < 1000)
    {
         Print("Bronze");
    }
    else if(status_level < 5000)
    {
         Print("Silver");
    }
    else
    {
         Print("Gold");
    }
}

Quindi vengono aggiunte funzionalità extra senza dover ricodificare e ripetere i test di base.

    
risposta data 11.11.2013 - 07:14
fonte
2

Se cerchi l'ereditarietà da classi concrete, scoprirai di non essere solo nella tua domanda.

In effetti, nella grande maggioranza dei casi, gli alberi ereditari hanno solo 2 o 3 livelli di profondità e solo le classi foglia (quelle che non sono usate come classe base) tendono ad essere concrete.

L'ereditarietà è principalmente uno strumento potente perché può portare alla luce interfacce o astrazioni comuni.

    
risposta data 11.11.2013 - 08:47
fonte
2

Penso che dovresti cercare su Google Coop e leggere i suoi eccellenti articoli sulla modellazione dei dati a colori. Una buona introduzione è link

Sebbene non menzionato esplicitamente nell'articolo precedente, un laboratorio di modellizzazione di Coad che ho frequentato ha utilizzato alcuni semplici test per decidere quando l'ereditarietà era il modo giusto per modellare i dati:

  • L'ereditarietà dovrebbe essere usata con parsimonia. Parte di questo ragionamento consiste nell'adattare linguaggi OO moderni che supportano solo l'ereditarietà singola.
  • L'ereditarietà deve essere utilizzata solo quando la sottoclasse si applica per tutta la vita di un oggetto. Ad esempio, Cat and Dog può estendere Animal, poiché un gatto non diventa mai un cane. Ma il Manager non dovrebbe estendere il Dipendente, in quanto un dipendente può diventare un manager. Manager è un dipendente che svolge un ruolo limitato nel tempo, non una relazione di ereditarietà. Coad ha inventato classi di ruolo e intervallo temporale per modellare tali ruoli.

Come si nota, cambiare il tipo di classe di un'entità in fase di esecuzione si sente sbagliato, ed è sicuramente l'odore del codice.

    
risposta data 02.12.2014 - 09:44
fonte
1

Non mi preoccuperei troppo dell'ereditarietà. Il polimorfismo riguarda più le interfacce che l'ereditarietà. (Okay, a volte una classe farà fa il lavoro e l'interfaccia è ridondante, ma in quel caso la classe sta davvero facendo un lavoro di interfaccia oltre al lavoro di una classe.)

Mi trovo a usare l'ereditarietà in due casi:

Nel primo, sto scrivendo una nuova classe, e una vecchia classe già fa circa la metà del lavoro di quella nuova. Quindi eredito dalla vecchia classe e il mio nuovo è per metà scritto. Più comunemente, noto che ho un sacco di classi con codice duplicato. Quindi porto quel codice in una singola classe e ne eredito tutti gli altri. (Nella maggior parte delle lingue puoi ereditare da un'altra classe, quindi devi stare un po 'attento a questo.)

Nel secondo ho una classe che fa il lavoro, ma in alcuni casi i suoi calcoli sono sbagliati. Così creo una nuova classe che eredita dal primo e sovrascrive il metodo sbagliato (in questo caso). (In alternativa, potresti rendere la prima classe più flessibile. Scegli il metodo che ti dà il codice più pulito. Alcune lingue rendono l'ereditarietà più semplice di altre.)

Sono sicuro che ci sono altre ragioni, ma per me quelle sono le più comuni.

    
risposta data 11.11.2013 - 16:34
fonte
0

Uno dei migliori esempi che ho letto descrive le cose in questo modo (parafrasato) usando uno zoo:

La classe base era Animal. Ha (proprietà) di dimensioni e colori. Potrebbe (metodi) mangiare, dormire e riprodursi.

Ora abbiamo diversi animali nello zoo.

Monkeys, Tigers, Dolphins.

Come classi, hanno ereditato da Animal, quindi avevano tutte dimensioni e colore e potevano mangiare, dormire e riprodursi.

Per i campi, consideriamo questi: I delfini hanno pinne e soffiature Le scimmie e le tigri hanno la coda Le tigri hanno artigli.

Per i metodi nella classe potremmo considerare queste cose: Le scimmie potrebbero anche (metodo) salire. Anche le tigri potevano arrampicarsi e cacciare. I delfini potrebbero nuotare. Quindi possono tigri.

Ora, i delfini non possono arrampicarsi, quindi arrampicarsi non sarebbe qualcosa che potresti spostare nella classe madre (animale), ma potresti metterli nella classe delle scimmie e delle tigri.

Sono TUTTI gli animali, quindi condividono tutte le proprietà SOME, ma ognuno di essi ha alcune proprietà diverse NON condivise da tutti gli animali.

Allo stesso modo, tutti possono fare alcune delle stesse cose - come mangiare e riprodursi, ma alcune cose che solo alcuni animali possono fare - come nuotare e arrampicarsi - quindi quelle cose sarebbero legate ad ogni specifico animale e NON TUTTI gli animali.

Nel tuo caso, esaminiamo la struttura Employee.

Tutti i Dipendenti avranno un ID dipendente, un nome, altre informazioni correlate sul dipendente.

Un dipendente generico potrebbe avere questi metodi: Timbrare ClockOut

Un impiegato sarebbe una classe ereditata dal dipendente avrebbe tutto quanto sopra ma forse potrebbe anche avere proprietà per certificazioni su alcuni pezzi di equipaggiamento - registri, scanner, ecc. - questi potrebbero essere gestiti anche in altri modi, ma lavorando in eredità lo semplificheremo qui.

Un impiegato potrebbe farlo Fare uno squillo ProcessReturns SearchInventory

Un Manager può essere ereditato dal Clerk o direttamente dal Dipendente ... forse potrebbero entrambi ereditare da un'interfaccia (supponendo che sia disponibile nella lingua di scelta) o da altre classi ... IClerkDuties - che definisce Ringup, ProcessReturns e SearchInventory ma non li implementa.

E quindi il gestore avrebbe IClerkDuties e IManagerDuties. IManagerDuties potrebbe avere cose come HireEmployee, BerateEmployee, FireEmployee, RateEmployeePerformance ...

MA, tutti questi sarebbero DIPENDENTI - e qualsiasi codice che debba influire o utilizzare la classe dei dipendenti potrebbe funzionare su o con tutti questi.

    
risposta data 01.12.2014 - 23:18
fonte

Leggi altre domande sui tag