Quando decidere di introdurre interfacce (classi di base astratte pure) in C ++?

0

Supponiamo che tu stia sviluppando una funzionalità e sei sicuro al 90% che la classe di implementazione rimarrà da sola. Se fossi in questa posizione in Java probabilmente non userei l'interfaccia adesso per mantenere le cose semplici. In Java è facile rifattorizzare il codice ed estrarre l'interfaccia in seguito.

In C ++ il refactoring non è sempre così facile. Potrebbe richiedere la sostituzione di valori con puntatori intelligenti (a causa dell'introduzione del polimorfismo) e altre attività non banali.

D'altra parte non mi piace molto l'idea di introdurre chiamate virtuali quando sono sicuro al 90% che non saranno necessarie. Dopo tutto, la velocità è uno dei motivi per preferire il C ++ su linguaggi più semplici.

    
posta Honza Brabec 03.07.2013 - 14:46
fonte

2 risposte

2

Vorrei sottolineare una cosa prima di condividere parte della mia esperienza. Si prega di fare attenzione a dire astratte classi di base e interfacce così liberamente a fianco. Gli ABC sono un concetto di linguaggio e Interface è un concetto di design. Ovviamente gli ABC sono molto utili nell'implementazione delle interfacce in c++ , tuttavia non penso che sia stato detto da nessuna parte (fonte altamente credibile) che sono pensati specificamente per questo. Ci sono altri modi per farlo! Per favore, leggi un link .

Detto questo, per rispondere alla tua domanda. Nella mia esperienza come regola generale dovresti sempre scrivere codice che ritieni sia stato progettato bene. Se pensi che i virtuals bloccheranno le tue prestazioni, seriamente non preoccuparti se non puoi dimostrare che è superiore al 10% se prevedi di gestire il tuo codice. Se è one-shot, spremere tutto, la progettazione del codice non ha importanza.

Purtroppo bisogna imparare come analizzare e profilare le applicazioni, a un livello che ritengo abbastanza elevato, per riprogettare correttamente il codice. Dovresti essere in grado di dire se hai bisogno di riorganizzare le filiali, eliminare i virtual o la gestione della memoria o trovare colli di bottiglia. Credo che oggigiorno siano molto più spesso legati all'hardware. Se osservi alcune domande relative alle prestazioni di SO c++ , vedrai che i colli di bottiglie hanno mentito in posti molto strani e solo le persone che hanno profilato il codice per le mancate cache o le previsioni delle branchie sono stati in grado di trovare le cause.

Devo dire che sono caduto vittima di ottimizzazioni pre-mature, oltre che di under e over-design. Ho cercato di implementare (diamine, ho implementato) cose intelligenti dove non erano necessarie, e solo complicato codice e sistema di interfaccia a un punto in cui non era divertente estendere i miei programmi. Per velocizzare il 5%, ho effettivamente ostacolato il processo di test fino al punto in cui ho dovuto trascorrere un giorno per implementare i test o testarlo manualmente dopo l'aggiunta di alcune nuove funzionalità. Sono anche passato a cose come get_x(){ return x;}; era virtual perché condivideva l'interfaccia con qualcosa che aveva una diversa gestione della memoria.

Per riassumere, per ora il mio consiglio sarebbe :

  1. Scrivi un codice che è ben progettato per le esigenze attuali (e forse per il futuro immediato). Non cercare di complicarlo troppo prima. È altrettanto brutto delle premature occasioni. Esperienza personale.

  2. Quando è appropriato, quindi refactoring e introduce modelli.

  3. Non preoccuparti delle prestazioni, a meno che tu non sia sicuro di averne bisogno. Impara ad analizzare il tuo codice ad alta qualità. Quindi lavora sui colli di bottiglia.

Ovviamente il suo lavoro probabilmente è il doppio rispetto a ottenere tutto giusto al 1 ° posto. Nel mio caso il problema è che mi manca il right modo 2 su 3 volte se non di più :). Credo che nel tempo un buon programmatore sia in grado di scegliere un buon approccio in anticipo più spesso.

Nei modelli contro gli ABC per le interfacce.

Penso che gli ABC siano più programmabili e leggibili, tuttavia mi piacerebbe avere il polimorfismo dei template se mi imbattessi in qualcosa del tipo:

...
virtual get_x() {return coords[0];};
virtual get_y() {return coords[1];};
virtual get_z() {return coords[2];};
...

void some_fancy_algorithm_that_work_on_coords(){
  // for example calculate distances between every pair of instances
  // that can provide 'ICoords' interface
}

Il polimorfismo di base di template I un compilatore semi-decente può eliminare i cast di oggetti impliciti e utilizzare direttamente metodi e proprietà di riferimento. È bene evitare i virtual e l'accesso intermedio, anche se tutto è racchiuso nella progettazione del codice.

    
risposta data 03.07.2013 - 16:01
fonte
2

Le interfacce sono una decisione di progettazione, quindi sono molto più importanti per ottenere diritti rispetto all'implementazione o alla convenienza. Il tuo design dovrebbe guidare le altre preoccupazioni, non il contrario. Ottieni il design giusto, decidi se introdurre le interfacce, quindi preoccupati di cosa significa nella tua lingua , C ++ o altro.

[refactoring] may require replacing values with smart pointers

I puntatori intelligenti dovrebbero essere una sostituzione drop-in; anche se questo non è possibile e devi usare i puntatori grezzi, dovresti usare il C ++ idiomatico in cui la vita degli oggetti è ben gestita, cioè è ovvio dove devi mettere delete s.

On the other hand I don't much like the idea of introducing virtual calls when I am 90% sure they won't be needed.

Sei sicuro di doversi preoccupare del sovraccarico delle chiamate virtuali? Hai profilato il tuo codice? La chiamata virtuale è sulla tua strada calda? Sei sicuro di aver esaurito altre opzioni per migliorare la velocità (che sono molto più efficaci), come algoritmi, strutture dati, compatibilità con la cache, inserendo elementi in L1?

    
risposta data 03.07.2013 - 15:20
fonte

Leggi altre domande sui tag