Come posso spiegare l'utilità dell'ereditarietà? [chiuso]


16

Quando si cerca di spiegare il concetto di ereditarietà in OOP, l'esempio comune è spesso l'esempio dei mammiferi. IMHO, questo è davvero un cattivo esempio, perché porterà i neofiti a usare questo concetto nel modo sbagliato. Inoltre, non è un progetto comune che dovranno affrontare nel loro lavoro di progettazione quotidiano.

Quindi, quale sarà un problema bello, semplice e concreto che viene risolto usando Ereditarietà?


1
"l'esempio comune è spesso i mammiferi"? Cosa intendi? Potete fornire un link, un riferimento o un preventivo per questo?
S.Lott


3
Quale sarebbe il miglior esempio reale per spiegare l'utilità del fondo fiduciario per bambini di Inheritance = Bill Gates?
Martin Beckett,

1
@Chris: in che modo questa domanda non è costruttiva? Stai dichiarando che un richiedente e 14 risponditori stanno sprecando il tempo di tutti?
Dan Dascalescu,

@DanDascalescu - è stato chiuso due anni fa in risposta a una bandiera che affermava "per favore considera la chiusura come non costruttiva: a giudicare dalle risposte che si accumulano su di essa, questo sembra un tipico elenco / domanda del sondaggio". Se ritieni che sia sbagliato, modificalo per chiarire che non lo è e lascia che la community decida tramite la coda di revisione di riapertura.
ChrisF

Risposte:


15

Non c'è niente di sbagliato in un esempio puramente accademico come i mammiferi. Mi piace anche l'esempio rettangolo / quadrato perché indica perché le tassonomie del mondo reale non si traducono sempre direttamente nella relazione di eredità che ti aspetteresti.

Secondo me, l'esempio più canonico di tutti i giorni è un toolkit GUI. È qualcosa che tutti hanno usato, ma che i principianti potrebbero non aver ragionato su come lavorano sotto il cofano. Puoi parlare di quali comportamenti sono comuni a tutti i contenitori, tutti i widget, eventi, ecc. Senza richiedere una conoscenza dettagliata di una determinata implementazione.


5
+1 per i toolkit GUI ... e mi piace anche l'esempio delle forme, con una forma come base con un disegno minimo () e le forme discendenti con disegni personalizzati ().
Yati Sagade,

14

Il mio esempio nel mondo reale è il modello di dominio di una semplice applicazione per le risorse umane. Dico che possiamo creare una classe base chiamata Employee , perché ovviamente anche i manager sono impiegati.

public class Employee
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public int Code { get; set; }

    public string GetInsuranceHistory()
    {
        // Retrieving insurance history based on employee code.
    }
}

Quindi spiego che gli sviluppatori sono impiegati , i tester sono impiegati , i project manager sono impiegati . Pertanto, tutti possono ereditare dalla classe dei dipendenti.


2
Per mostrare i vantaggi dell'ereditarietà, potrebbe essere interessante anche mostrare in che modo gli sviluppatori differiscono dai dipendenti. Se non c'è alcuna differenza, non è necessario creare una classe Developer.
David,

3
Sembra che Employeepotrebbe essere anche una abstractlezione.
StuperUser

+1 questo è l'esempio usato dalla maggior parte dei miei insegnanti e mi è davvero piaciuto. aveva perfettamente senso e forniva un esempio del mondo reale su come usare l'eredità.
David Peterman,

18
Solo che questo non funziona mai in pratica perché c'è sempre almeno una persona che deve essere sia a Developerche a Tester. Un'altra situazione simile è un database di contatti in cui hai Customere Supplier, ma come chiunque ti abbia creato un tale sistema ti dirà, c'è sempre un caso in cui un Companyè entrambi. Ecco perché la maggior parte di questi esempi ti porta nella direzione sbagliata.
Scott Whitlock,

11

Incapsula ciò che varia ... mostra loro un modello di modello , dimostra l'utilità dell'eredità inserendo comportamenti comuni in una classe base e incapsulando comportamenti variabili in sottoclassi.

UI controlse Streamssono anche un ottimo esempio per l'utilità dell'eredità.


Penso che la fabbrica sarebbe un esempio migliore.
Let_Me_Be

1
@Let_Me_Be: penso che il rapporto tra una fabbrica e l'eredità sia troppo indiretto nella sua natura. Certo, produce tipi concreti e restituisce tipi astratti / base, ma potrebbe anche restituire solo un tipo di interfaccia! Imho non è meglio del classico esempio animale.
Falcon

@Let_Me_Be: Inoltre, una fabbrica astratta è un esempio piuttosto complesso che coinvolge diverse gerarchie ereditarie (una per gli articoli, una per le fabbriche). Penso che sia un buon uso dell'eredità, ma non un buon e semplice esempio.
Falcon,

3

Ricorda

Ogni istanza di un oggetto è un esempio concreto dell'utilità dell'eredità!

Se intendi specificamente l' eredità di classe , ora ti trovi nel mondo delle tassonomie, e queste varieranno drasticamente in base agli obiettivi del sistema che le utilizzano. L'esempio di animali / mammiferi usa una tassonomia comune e speriamo familiare dalla biologia, ma è (come hai detto tu) quasi inutile per la stragrande maggioranza dei problemi di programmazione.

Quindi prova qualcosa di universale: la nozione di programma. Ogni programma inizia, viene eseguito e termina. Ogni programma ha un nome e parametri opzionali da riga di comando. Quindi una classe Program di base sarebbe molto utile, per avviare l'esecuzione, afferrare ed elaborare gli argomenti della riga di comando, eseguire la logica principale e chiudere con grazia.

Ecco perché così tanti linguaggi di programmazione orientati agli oggetti forniscono una classe Program o qualcosa che si comporta esattamente come una classe Program.


Quindi, programmi un programma che contiene molti programmi? :) Nella mia esperienza, gli oggetti di programmazione sono quasi sempre singoli che non hanno eredità, quindi non sono il miglior esempio.
keppla,

@keppla: hai mai usato Java o .NET? .NET ha una classe di programma esplicita, Java è implicita. Non sono singoli
Steven A. Lowe,

ho usato Java, intorno alla versione 1.4.2. Allora, c'era solo il vuoto statico principale, quindi credo che sia cambiato un po '. Quale sarebbe un motivo tipico per avere più di un'istanza della classe Programm?
keppla,

@keppla: il vuoto statico principale di java rende implicitamente la classe entry rappresentativa del programma. Ogni utente che esegue il programma ne crea una nuova istanza. In questo momento, ho tre istanze di Google Chrome in esecuzione, quattro documenti Word, tre blocchi note e due esploratori di Windows. Se fossero tutti singoli non sarei mai in grado di farlo.
Steven A. Lowe,

1
penso che stai allungando un po 'la definizione. class Programm { public static void main(String[] args) { system.out.println('hello world'); }}è un programma Java minimo. Quando lo chiamo, non c'è istanza di Program. Il programma non eredita da nulla. Quando avvio 3 Processi (come fai con crhome), potrebbero esserci 3 Programmi, ma nelle loro singole aree di memoria, c'è ancora un solo Programma. Imho, singleton implica "Solo un'istanza per processo", non per macchina. In tal caso, sarebbe impossibile creare dei singleton, nulla ti impedisce di eseguire due volte un codice.
keppla,

3

Sto lavorando con le telecamere al lavoro. Abbiamo dispositivi che si collegano a diversi modelli, quindi abbiamo una "classe di telecamere" astratta e ogni modello eredita da questa classe per supportare funzionalità specifiche di quella telecamera. È un esempio del mondo reale e non è difficile da capire.


2
Questo potrebbe rompersi se hai, ad esempio un modello che è sia a Camerache a Phone(come facciamo tutti adesso nelle nostre tasche). Da quale classe base dovrebbe ereditare? O non dovrebbe semplicemente implementare entrambe le interfacce ICamerae IPhone? (ah ah)
Scott Whitlock,

2
@Scott: non è possibile implementare l'interfaccia IPhone o verrai citato in giudizio da Apple.
Mason Wheeler,

3

Esempio di elementi di chimica

Questo è un altro esempio emerso dal mio cervello:

classe Element_
{
    doppio peso atomico; // Peso atomico dell'elemento
    doppio atomicNumber; // Numero atomico di elemento
    Proprietà stringa; // Proprietà dell'elemento
    // Altri, se presenti
}


la classe Isotope estende Element_ // Possono esistere isotopi dell'elemento
{
    doppia emivita;
   // Altri se presenti

}

2
Sebbene atomicNumber possa (dovrebbe?) Probabilmente essere un numero intero ...
Andrew

Non userei l'eredità per quello. isotopenon è un caso speciale di Elemenet. Preferirei avere una Elementproprietà Isotope.
CodesInChaos,

2

Gli esempi del mondo reale quasi sempre sbagliano perché forniscono esempi in cui esiste sempre la possibilità che qualcosa sia entrambi TypeAe TypeBma la singola gerarchia ereditaria di molte lingue non lo consente.

Più programma, più mi allontano dall'eredità.

Anche qui la parola "ereditare" è usata impropriamente. Ad esempio, erediti circa il 50% dei tratti di tuo padre e il 50% dei tratti di tua madre. Davvero il tuo DNA è una composizione di metà del DNA di tuo padre e metà del DNA di tua madre. Questo perché la biologia in realtà favorisce la composizione rispetto all'eredità , e dovresti farlo anche tu.

La semplice implementazione di interfacce o, meglio ancora, la "tipizzazione anatra", oltre all'iniezione di dipendenza, è una cosa molto migliore per insegnare alle persone che non conoscono la programmazione orientata agli oggetti.


1

Vorrei solo mostrare loro un esempio di vita reale. Ad esempio, nella maggior parte dei framework dell'interfaccia utente si ottiene una sorta di classe "Finestra di dialogo" o "Finestra" o "Controllo".


1

Un buon esempio è la funzione di confronto nell'ordinamento:

template<class T>
class CompareInterface {
public:
   virtual bool Compare(T t1, T t2) const=0;
};
class FloatCompare : public CompareInterface<float> { };
class CompareImplementation : public FloatCompare {
public:
   bool Compare(float t1, float t2) const { return t1<t2; }
};
template<class T>
void Sort(T*array, int size, CompareInterface<T> &compare);

L'unico problema è che i neofiti troppo spesso pensano che le prestazioni siano più importanti del buon codice ...


0

Il mio esempio del mondo reale è un veicolo:

public class Vehicle
{
    public Vehicle(int doors, int wheels)
    {
        // I describe things that should be
        // established and "unchangeable" 
        // when the class is first "made"
        NumberOfDoors = doors;
        NumberOfWheels = wheels;
    }

    public void RollWindowsUp()
    {
        WindowsUp = true;
    }

    // I cover modifiers on properties to show
    // how to protect certain things from being
    // overridden
    public int NumberOfDoors { get; private set; }
    public int NumberOfWheels { get; private set; }

    public string Color { get; set; }
    public bool WindowsUp { get; set; }
    public int Speed { get; set; }
}

public class Car : Vehicle
{
    public Car : base(4, 4)
    {

    }
}

public class SemiTruck : Vehicle
{
    public SemiTruck : base(2, 18)
    {

    }
}

Questo esempio può essere dettagliato come desideri e ci sono tutti i tipi di proprietà associate ai veicoli per spiegare l'uso di eventuali modificatori che potresti voler insegnare.


2
Ho sempre odiato usare i veicoli come esempio, in quanto non avvicina un nuovo programmatore alla comprensione di come l'ereditarietà può essere utilizzata per migliorare il codice. I veicoli sono macchine immensamente complicate che richiamano alla mente molte idee non astratte nella mente del non programmatore. Cercare di descriverlo nel codice fa credere al novizio medio che ci sono molti dettagli esclusi dall'esempio e dà la sensazione che non siano più vicini a far funzionare qualcosa. Lo dico per esperienza, poiché è esattamente quello che ho provato quando qualcuno ha cercato di usare i veicoli per spiegarmelo.
riwalk

@ Stargazer712: Uso i veicoli principalmente perché possono essere complicati o semplici come desideri. Lascio al giudizio dell'istruttore determinare il livello del suo allievo. Ho spiegato OOP di base a mia moglie (che non ha esperienza di programmazione) usando le semplici proprietà di un veicolo che descrivono le basi comuni. Tutti i veicoli hanno porte, tutti i veicoli hanno ruote, ecc. L'esempio dell'oggetto non può essere incolpato di un piano di lezioni scadente.
Joel Etherton,

tua moglie non stava cercando di scrivere codice. Posso molto tranquillamente dire che gli esempi di veicoli ha fatto nulla per aiutarmi a capire l'ereditarietà. Qualunque cosa tu usi per descrivere l'eredità, deve essere completa e pratica . L'obiettivo non è descriverlo in modo tale che un non programmatore possa capirlo. L'obiettivo è descriverlo in modo tale che un programmatore inesperto possa usarlo , e l'unico modo per farlo è mostrare agli esempi inesperti di come un programmatore professionista lo userebbe.
riwalk

@ Stargazer712: metterei la tua incapacità di comprendere inizialmente l'eredità su un piano di lezioni scadente. Ho anche usato dei veicoli per spiegare l'eredità ai ragazzi con cui lavoro, e non ho mai avuto problemi con l'idea che mi è venuta in mente. Secondo me, se un programma di lezioni è completo e correttamente costruito, un oggetto veicolo è completo e pratico. 1 ragazzo a caso su Internet non lo cambierà di fronte ai circa 30 stagisti e sviluppatori junior a cui ho insegnato OOP. Se non ti piace il veicolo, vota verso il basso e vai avanti.
Joel Etherton,

Come desideri ....
riwalk

0

Questo esempio non-mammifero, non-uccello, non-pesce potrebbe aiutare:

public abstract class Person {

    /* this contains thing all persons have, like name, gender, home addr, etc. */

    public Object getHomeAddr() { ... }

    public Person getName() { ... }

}

public class Employee extends Person{

    /* It adds things like date of contract, salary, position, etc */

    public Object getAccount() { ... }

}

public abstract class Patient extends Person {
    /* It adds things like medical history, etc */
}

Poi

public static void main(String[] args) {

    /* you can send Xmas cards to patients and employees home addresses */

    List<Person> employeesAndPatients = Factory.getListOfEmployeesAndPatients();

    for (Person p: employeesAndPatients){
        sendXmasCard(p.getName(),p.getHomeAddr());
    }

    /* or you can proccess payment to employees */

    List<Employee> employees = Factory.getListOfEmployees();

    for (Employee e: employees){
        proccessPayment(e.getName(),e.getAccount());
    }       

}

NOTA: non dire il segreto: la persona estende Mammal.


1
Funziona fino a quando uno dei tuoi dipendenti è anche un paziente.
Scott Whitlock,

In questo caso, penso che abbia più senso dichiarare Paziente e Dipendente come interfacce anziché classi astratte. Ciò ti dà la flessibilità di avere Person implementare più interfacce.
Jin Kim,

@JinKim - Sono assolutamente d'accordo, questo è l'approccio migliore.
Scott Whitlock,

@JinKim Non sono esclusivi. Trattate una persona come un dipendente o un paziente in un dato momento, ma non allo stesso tempo. Due interfacce vanno bene, ma allora come si chiama una classe concreta che implementa entrambe, EmployeePatient? Quante combinazioni avrai?
Tulains Córdova,

Puoi chiamare la classe concreta come vuoi. Se il codice prevede di trattare solo con Dipendenti, si dichiara il riferimento come Dipendente. (ovvero Impiegato dipendente = nuova persona ();) Se il codice prevede di trattare solo con un paziente, si dichiara il riferimento come paziente. Raramente vuoi dichiarare un riferimento direttamente come classe concreta.
Jin Kim,

0

Che ne dici di una gerarchia di espressioni algebriche. È buono perché include sia l'eredità che la composizione:

+--------------------+------------------------+
| Expression         |<------------------+    |
+--------------------+----------+        |    |
| + evaluate(): int  |<---+     |        |    |
+--------------------+    |     |        |    |
          ^               |     |        |    |
          |               |     |        |    |
   +--------------+  +---------------+  +-------------+  ...
   | Constant     |  | Negation      |  | Addition    |
   +--------------+  +---------------+  +-------------+
   | -value: int  |  |               |  |             |
   +--------------+  +---------------+  +-------------+
   | +evaluate()  |  | +evaluate()   |  | +evaluate() |
   | +toString()  |  | +toString()   |  | +toString() |
   +--------------+  +---------------+  +-------------+

   Addition(Constant(5), Negation(Addition(Constant(3),Constant(2))))
   (5 + -(3 + 2)) = 0

Ad eccezione dell'espressione radice Costante, tutte le altre espressioni sono sia Espressione che contengono una o più espressioni.


-1

Userò gli uccelli come esempio

come pollo, anatra, aquila

Spiegherò entrambi che hanno artigli, becchi e ali ma i loro attributi sono diversi.

I polli non possono volare, non sanno nuotare, possono mangiare vermi, possono mangiare cereali

Le anatre non possono volare, possono nuotare, possono mangiare cereali, non possono mangiare vermi

L'aquila può volare, non sa nuotare, può mangiare vermi, non può mangiare cereali


4
Una volta ho letto che la composizione e le interfacce sono probabilmente il modo migliore per trasmettere questo tipo di concetto, ovvero volare, nuotare, ecc.
Dreza,

Un'anatra non può volare ?!
Adam Cameron,

-3

Il tuo tipico clone rails fornisce molti esempi pratici : hai la classe (astratta) del modello di base, che incapsula tutta la manipolazione dei dati e hai la classe del controller di base, che incapsula tutte le comunicazioni HTTP.


Vuoi elaborare perché questa risposta è negativa?
keppla,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.