Esiste una logica di utilizzo diversa per le classi / interfacce astratte in C ++ e Java


13

Secondo Herb Sutter si dovrebbero preferire interfacce astratte (tutte le funzioni virtuali pure) a classi astratte in C ++ per disaccoppiare l'implementazione il più possibile. Mentre personalmente trovo questa regola molto utile, recentemente mi sono unito a un team con molti programmatori Java e nel codice Java questa linea guida non sembra esistere. Le funzioni e le loro implementazioni si trovano molto frequentemente in classi astratte. Quindi ho sbagliato Herb Sutter anche per C ++ o c'è una differenza generale nell'uso delle funzioni astratte in C ++ rispetto a Java. Le classi astratte con codice di implementazione sono più sensibili in Java che in C ++ e se sì perché?


1
Avevo dei dubbi e alla fine l'ho messo qui perché potrebbe essere dovuto ad alcuni principi di progettazione che mi mancano sul java oo. Quindi non si tratta di un consiglio generale ma di più di ciò che è giusto e ciò che è sbagliato usare la lingua
Martin

Le interfacce sono pensate per essere puramente virtuali. L'idea delle classi astratte è che sono parzialmente implementate, e spetta all'implementazione colmare le lacune senza ripetere inutilmente il codice (ad esempio, perché avere write (byte) e write (int) in ogni sottoclasse quando si può avere il abstract class call write (byte) da write (int))

1
Possibilmente correlato: stackoverflow.com/q/1231985/484230 fornisce un motivo per preferire le classi astratte in Java. Per C ++ questo motivo non sembra essere vero a causa dell'esistenza di funzioni gratuite che possono aggiungere funzionalità a livello di interfaccia
Martin

1
Penso che la Regola d'oro sia "rendere astratte le classi non foglia", ma ciò non rende alcun requisito "solo puro" o "vuoto".
Kerrek SB,

1
Se funziona per te, funziona per te. Davvero non vedo perché le persone vanno nel panico quando il loro codice non aderisce più alle ultime opinioni.
James,

Risposte:


5

OOP ha composizione e sostituzione.

Il C ++ ha ereditarietà multipla, specializzazione di template, incorporamento e semantica value / move / pointer.

Java ha ereditarietà e interfacce singole, incorporamento e semantica di riferimento.

Il modo comune in cui la scuola OOP utilizza queste lingue è utilizzare l'eredità per la sostituzione degli oggetti e l'incorporamento per la composizione. Ma hai anche bisogno di un antenato comune e di un modo per eseguire il cast di runtime (in C ++ viene chiamato dynamic_cast, in Java è solo chiedere un'interfaccia da un altro).

Java fa tutto questo con la sua java.lang.Objectgerarchia radicata. Il C ++ non ha una radice comune predefinita, quindi dovresti almeno definirla, per arrivare alla stessa "immagine" (ma questo sta limitando alcune possibilità del C ++ ...).

Successivamente, la possibilità di avere un polimorfismo in fase di compilazione (pensa a CRTP) e un valore semantico può offrire anche altre alternative al modo in cui il concetto di "oggetto OOP" può essere portato in un programma C ++.

Puoi persino immaginare l'eresia di usare l'incorporamento e la conversione implicita per gestire la sostituzione e l'eredità privata per gestire la composizione, invertendo di fatto il tradizionale paradigma scolastico. (Certo, in questo modo è 20 anni più giovane dell'altro, quindi non aspettarti un ampio supporto da parte della comunità nel farlo)

Oppure puoi immaginare una base virtuale comune a tutte le classi, interfaccia modulo (nessuna implementazione) a classi finali (completamente implementate) che attraversano interfacce parzialmente implementate e cluster di interfacce uniformi, usando "dominanza" come invio da interfaccia a implementazioni attraverso un "multi stack -parallelogramma "schema ereditario.

Confrontando OOP con Java e C ++ supponendo che ci sia un solo modo OOP sta limitando le capacità di entrambe le lingue.

Costringere C ++ ad aderire rigorosamente agli idiomi della codifica Java sta denaturando C ++ poiché costringere Java a comportarsi come un linguaggio simile al C ++ sta denaturando Java.

Non è una questione di "sensibilità" ma di diversi "meccanismi di aggregazione" che le due lingue hanno e un modo diverso di combinarle che rende un linguaggio più redditizio in una lingua rispetto all'altra e viceversa.


1
Penso che questa risposta sia molto interessante, dal momento che descrive in modo succinto le caratteristiche del linguaggio come uno strumento per i principi di oo e design solo come un aiuto e non come una dottrina. Tuttavia non hai bisogno di una radice comune se vuoi fare oo in c ++. Questo è semplicemente sbagliato, anche per il fatto che hai operatori e modelli (che sono un'alternativa molto potente al design dell'albero principale di java come hai anche sottolineato). A parte questo, i tuoi punti sono i più utili in tutte le risposte
Martin

1
@Martin: In "senso tecnico" hai ragione, ma se hai bisogno di un polimorfismo di runtime (perché il tipo effettivo degli oggetti istanziati dipende dall'input del programma) una "radice" ('a' è un articolo, non una scorciatoia per " uno e solo ") è ciò che rende" cugini "tutti gli oggetti e la gerarchia run-time-walkable. Radici diverse originano origini diverse non correlate tra loro. Se questo è "buono" o "cattivo" è una questione di contesto, non di linguaggio.
Emilio Garavaglia,

È vero. Pensavo che ti stavi riferendo al fornire artificialmente una radice generale per un intero programma c ++ e l'ho visto come un difetto che non è presente, rispetto a Java. Ma dopo la modifica chiarisci il punto. Grazie ancora
Martin

12

Il principio vale per entrambe le lingue, ma non stai facendo un confronto equo. Dovresti confrontare le classi astratte pure in C ++ con le interfacce Java.

Anche in C ++, puoi avere classi astratte che hanno alcune funzioni implementate, ma derivano da una classe astratta pura (nessuna implementazione). In Java, avresti le stesse classi astratte (con alcune implementazioni), che possono derivare da interfacce (nessuna implementazione).


Quindi, quando preferiresti una classe astratta rispetto a una classe di interfaccia in c ++. Ho sempre optato per l'interfaccia più le funzioni non membro in c ++.
Martin,

1
@Martin che dipende dal design. Fondamentalmente, preferisco sempre un'interfaccia. Ma le regole " sempre " hanno delle eccezioni ...
Luchian Grigore,

Abbastanza vero, ma nel codice Java vedo classi astratte che rappresentano in gran parte la maggioranza. Ciò potrebbe essere dovuto al fatto che le funzioni libere che lavorano su interfacce non sono possibili in Java?
Martin,

3
Le funzioni gratuite di @Martin non sono affatto possibili in Java, quindi potrebbe essere un motivo, sì. Buon posto! Ha risposto alla tua domanda! Puoi aggiungere una risposta da solo, penso che sia così.
Luchian Grigore,

4

Generalmente gli stessi principi OO valgono per Java e C ++. Tuttavia, una grande differenza è che C ++ supporta l'ereditarietà multipla mentre in Java è possibile ereditare solo da una classe. Questo è il motivo principale per cui Java ha interfacce, credo, per integrare la mancanza di eredità multipla e probabilmente per limitare ciò che si può fare con essa (poiché ci sono molte critiche sull'abuso dell'eredità multipla). Quindi, probabilmente nella mente di un programmatore Java, esiste una distinzione più forte tra classi astratte e interfacce. Le classi astratte vengono utilizzate per condividere ed ereditare il comportamento mentre le interfacce vengono semplicemente utilizzate per aggiungere funzionalità extra. Ricorda, in Java puoi ereditare da una sola classe, ma puoi avere molte interfacce. In C ++, tuttavia, le classi astratte pure (ovvero una "interfaccia C ++") lo sono utilizzato per condividere ed ereditare il comportamento diversamente dallo scopo di un'interfaccia Java (sebbene sia ancora necessario implementare le funzioni), quindi l'utilizzo è diverso dalle interfacce Java.


0

A volte ha senso avere una implementazione predefinita. Ad esempio un metodo PrintError generico (stringa msg) applicabile a tutte le sottoclassi.

virtual PrintError(string msg) { cout << msg; }

Può ancora essere sovrascritto se richiesto, ma è possibile salvare il client un po 'di seccatura permettendo loro di chiamare semplicemente la versione generica.

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.