2012-09-26 16 views
5

Ho le classi A e B. Ora ho bisogno di scrivere una nuova classe C che userà alcuni campi e metodi sia in A che in B ma non tutti. (Userò circa il 50% delle cose da A e B).Ereditarietà solo per il riutilizzo del codice C++

In questo momento sto pensando di ereditare sia da A che da B. Ma questo farà sì che C contenga molti campi e metodi che non hanno alcun significato.

Fondamentalmente sto usando eredità solo per scopi di riutilizzo del codice, perché altrimenti dovrò copiare e incollare molte righe di codice da A e B.

è questa pratica davvero male? C'è un approccio diverso?

Grazie!

+0

Grazie per le risposte ragazzi! Li apprezzo davvero. Il motivo per cui ho preso in considerazione l'utilizzo dell'ereditarietà invece della composizione è dovuto anche alla necessità di modificare alcuni comportamenti in A e B. Inoltre non desidero veramente modificare A e B. – user1657624

risposta

2

È male. Usa l'ereditarietà solo quando logicamente ha senso.

Usa composizione invece (private ereditarietà è una forma di composizione, ma meglio andare vecchia scuola):

class C 
{ 
    A a; 
    B b; 
} 

Se C non è composto da A e B, si può anche tenere i puntatori, invece, in modo la dimensione di C non aumenta.

3

L'ereditarietà deve utilizzare il paradigma "è un".
Significa che C deve ereditare da A, B se è una sorta di A e B.
Se si utilizza già un'ereditarietà multipla, è possibile che si desideri suddividere A e B in più classi ciascuna che si occupa di un piccolo frammento del proprio codice, e quindi eredita C solo da ciò che ti serve, in questo modo C occuperà solo lo spazio di cui ha bisogno (supponendo che A e B abbiano molti membri).
Ricorda che la dimensione della tua classe non è influenzata dai metodi dei membri, ma solo dai dati dei membri.

+1

In C++, * public * inheritance significa "is-a" . Altri livelli di ereditarietà significano qualcosa di diverso ... come "è-implementato-in-termini-di". – cHao

+0

@cHao - è vero, ma anche con l'ereditarietà privata - C che ha accesso a parti di codice di cui non ha bisogno è una cattiva pratica. Inoltre - se A e B hanno molti membri di dati, la dimensione di C sarà ancora più grande di quanto dovrebbe essere. –

6

L'ereditarietà non ha il concetto di "è metà di un", quindi non è sicuramente il modo di andare qui. Sembra che sia un caso primo per la composizione.

Suona come A e B hanno più di una "funzione" poiché la metà di ognuna è sufficiente per comporre uno C.

io non sono sicuro che questo è applicabile nel tuo caso, ma considero rompendo un in due parti, e A1A2 con la funzionalità necessaria per C in A1. Quindi fare lo stesso per B in B1 e B2.

A sarà quindi una composizione di A1 e A2, B sarà una composizione di B1 e B2 e C sarà una composizione di A1 e B1. Nessuno di loro con funzionalità o dati non necessari.

3

Come Herb Sutter e Andrei Alexandrescu spiegano nel loro libro C++ Coding Standards (item 34), l'ereditarietà è uno dei rapporti più stretti che C++ consente di utilizzare tra due classi - E 'secondo solo a un rapporto friend tra le classi.

Secondo lo S.O.L.I.D principles of OO, un progetto di successo dovrebbe mirare allo libero accoppiamento tra classi - questo diminuisce mantenendo le dipendenze al minimo; che a sua volta implica che l'ereditarietà sia uno strumento che dovrebbe essere usato con cautela.

Naturalmente, le dipendenze in genere bisogno di esistere da qualche parte , quindi un compromesso ragionevole può essere ereditato dalle classi senza l'attuazione a tutti (Analogo a cosiddetti interface s in linguaggi come C# e Java). per esempio.

class IDriveable 
{ 
public: 
    virtual void GoForward() = 0; 
    virtual void GoBackward() = 0; 
}; 

class Car : public IDriveable { /* etc. */ }; 
class Bus : public IDriveable { /* etc. */ }; 
class Train : public IDriveable { /* etc. */ }; 

Con questo approccio, se si dispone di elementi di codice reimpiegati tra diversi pilotabile classi, è in genere utilizzato composizione o qualche altra relazione debole per eliminare codice ripetuti.
ad es. forse si vuole riutilizzare il codice per TurnLeft per un Bus e un Car ma non un Train dove girando a sinistra è illogico, così TurnLeft potrebbe finire in una classe separata, che è membro-di Bus e Car.

  • Inoltre, qualsiasi funzionalità che potrebbero aver bisogno di conoscere tutte le classi vagamente legate sarebbe esterno alla gerarchia di classe, solo a conoscenza dell'interfaccia/base, piuttosto che i grattacapi dettagli di implementazione.

Il risultato finale può essere una piccola quantità di codice aggiuntivo per la composizione, ma spesso un progetto meno complesso e in genere uno più facile da gestire. Non ci sono regole durissime per la progettazione di codice come questo dato che dipende interamente dai problemi unici che stai cercando di risolvere.

ci sono altri modi per riutilizzare il codice senza accoppiamento stretto troppo - i modelli consentono di implicitamente definire le interfacce senza classi vuote che contengono puri virtual funzioni (modelli forniscono una maggiore sicurezza di tipo - che è un buona cosa, ma sono sintatticamente un un po 'più complesso);

E ci sono modi in cui è possibile utilizzare std::function e lambda per riutilizzare il codice in uno stile più funzionale, anche in questo caso non ci sono solitamente dipendenze strette quando si passa intorno agli oggetti funzione.

0

suona come si potrebbe beneficiare di refactoring del codice:

Invece di questo:

class A 
{ 
    virtual void foo(); 
    virtual void bar(); 
}; 

class B 
{ 
    virtual void biz(); 
    virtual void baz(); 
}; 

class C : public A, public B 
{ 
    virtual void foo() { /* my stuff */ } 
    virtual void biz() { /* my other stuff */ + 

    // but I don't need bar or baz! 
}; 

pensare di dividere le concettualmente diverse parti di A e B che si desidera in C:

class core_A 
{ 
    virtual void foo(); 
}; 

class A : public core_A 
{ 
    virtual void bar(); 
}; 

class core_B 
{ 
    virtual void biz(); 
}; 

class B : public core_B 
{ 
    virtual void baz(); 
}; 

class C : public core_A, public core_B 
{ 
    virtual void foo() { /* my stuff */ } 
    virtual void biz() { /* my other stuff */ + 

    // Great, I don't have bar or baz! 
}; 
Problemi correlati