Correzione classe Errori di progettazione gerarchia a causa di errata ereditarietà

0

Ho un codice come questo. La mia classe derivata man , potrebbe non avere una funzione membro dichiarata pura virtuale più in alto nella gerarchia. per esempio. WagTail qui

class mammal{
public:
    virtual void WagTail() = 0;
    virtual void BreastFeed() { };
    ~mammal(); 
};

class Terrestrials : public mammal{
public:
    virtual void sunbathe() = 0;
};

class cat : public Terrestrials {
    void sunbathe() override final  {
        cout << "My owner made me stand in the sun";
    }
    void WagTail() override final   {
        cout << "Wagging Tail";
    }
};

class Man : public Terrestrials {
    void sunbathe() override final  {
        cout << "Life is good!";
    }
    void WagTail() override final   {
        // Wait, Wha??? ERROR!!!!
    }
}; 

Qual è il modo migliore per correggere un errore come questo senza riscrivere l'intera gerarchia?

    
posta The Vivandiere 02.04.2015 - 20:13
fonte

4 risposte

4

Il problema fondamentale è che una funzione pura virtuale è stata aggiunta molto in alto in una gerarchia di classi che non tutte le sottoclassi concepibili possono plausibilmente supportare. Questo è il motivo per cui si dovrebbe essere molto attenti a definire gerarchie di classi profonde. Vedo un paio di approcci.

Un approccio è semplicemente fornire un'implementazione vuota di WagTail per la classe Man. Spero che questo non rompere nulla. E dopo tutto, i primati in genere hanno la coda, solo per le persone che hanno appassito in un coccige interno (coccige) perché non hanno fornito alcun beneficio. E alcune persone nascono con " code ".

Un altro è spostare la coda in una classe separata, in realtà solo un'interfaccia, e la maggior parte dei Terrestri eredita da essa. In questo modo anche altre classi, come Rettili e Pesci, possono avere code e un metodo WagTail. In generale, l'ereditarietà multipla delle interfacce è preferibile alle gerarchie di classi profonde.

Nota storica : negli anni '80 e nei primi anni '90, prima che i compilatori Java e C ++ non supportassero ampiamente l'ereditarietà, le gerarchie di classi profonde erano piuttosto comuni. Microsoft MFC era abbastanza profondo , così come lo era di Apple < a href="http://en.wikipedia.org/wiki/MacApp"> MacApp .

    
risposta data 02.04.2015 - 20:25
fonte
4

In realtà, Randall Cook ha dato un'ottima risposta qui, ma vorrei aggiungere qualcosa. Supponendo che implementerai "WagTail" per "man", il modo corretto di implementarlo dipende dalle aspettative del codice che chiama quel metodo sui mammiferi. Se il chiamante si aspetta che venga generata qualche tipo di comportamento di errore o eccezione, è possibile implementarla. Se il chiamante non se lo aspetta, puoi implementarlo vuoto.

Ecco perché per qualsiasi interfaccia o pura classe di base virtuale, ci dovrebbe essere un contratto da qualche parte, una definizione formale o informale del comportamento previsto. Senza un contratto, puoi solo indovinare cosa è necessario per non violare il Principio di sostituzione di Liskov.

    
risposta data 02.04.2015 - 20:36
fonte
2

Questo è un caso di una violazione del Principio di sostituzione di Liskov .

La tua classe di mammiferi sembra essere stata erroneamente chiamata, non tutti i mammiferi hanno la coda come potresti aver notato.

Potresti riuscire a risolverlo con ereditarietà multipla.

class Waggable
    + WagTail

class Terrestrial
    + SunBathe

class Cat : Terrestrial, Waggable

class Man : Terrestrial

Anche se non sono sicuro di cosa intendi, non riscrivendo la gerarchia. La gerarchia è il problema, quindi non so come puoi "aggiustarlo" senza cambiarlo.

    
risposta data 02.04.2015 - 20:24
fonte
0

In questo caso, scodinzolare è solo l'espressione di qualcosa di più fondamentale (eccitazione?). Rinomina WagTail ad alto livello in qualcosa di più "semplice" ( ExpressExcitement ?), Aggiorna il codice di richiamo e invoca semplicemente il WagTail metodi / codice della sottoclasse pertinente dai loro metodi ExpressExcitement .

Quel potrebbe avere molto lavoro (potrebbe anche essere una ricerca / sostituzione molto veloce); ma probabilmente vale la pena di rendere il tuo denominatore comune più specifico / meno specifico per l'implementazione.

... Immagino che questo potrebbe fallire il tuo requisito "senza riscrivere l'intera gerarchia". Ma, direi che il requisito è sbagliato.

    
risposta data 02.04.2015 - 20:54
fonte