Refactoring C ++ [chiuso]

5

So che ci sono alcuni altro domande sul refactoring di C ++ , ma nessuno soddisfa i miei bisogni.

Ho uno sfondo in programmazione Java e Python, ma ora mi sto avvicinando al C ++. Ho letto (e assolutamente amo) libri sulla codifica agile, sviluppo basato sui test (TDD), refactoring, ecc. Il primo che mi viene in mente è "Clean Code". Da questo ho imparato un sacco di cose: mantieni le mie funzioni corte, mantieni le mie lezioni brevi, mantieni nomi di variabili / classi coerenti, ecc.

Mi piace molto questo modo di lavorare: prima risolvi il problema, poi rifatta in piccole funzioni (e una funzione fa solo una cosa), aggiungendo sottoclassi, ecc.

Credo fermamente che questo modo di lavorare sia facilitato dai moderni IDE: aggiungi file, rinomina la funzione, rinomina le classi, ecc.

Ora sto codificando in C ++ con CMake all'interno di Qt Creator . Tutte queste cose sono più difficili. Anche aggiungere un file è "più difficile" (devo generare un file di intestazione, un file sorgente, aggiungerli a cmakelist.txt, eseguire di nuovo cmake, ecc.). Stai pensando di rinominare i file (devo modificare manualmente tutti gli include) o alcuni metodi di refactoring efficaci in Java con Eclipse non ha alcun costo ora mi dà un mal di testa.

Voglio dire ... non è "difficile" nel senso che sono "compiti difficili", ma la mente umana è piuttosto pigra: se posso fare un altro modo più semplice ..

In Java con alcuni IDE moderni come Eclipse .. refactor è la parte divertente; alcuni clic e voilà, è fatto. Lo faccio ogni volta che passo un test. È facile, divertente e mi piace leggere il mio codice pulito con piccole funzioni e nomi carini.

Ora tutto è più difficile soprattutto perché non ci sono strumenti che possono farlo senza costi. Mi spoglio in atteggiamenti cattivi guidati dalla pigrizia: scrivere diverse classi in un unico file .hong super-lungo perché non voglio creare nuovi file, passare ore a modificare classi / funzioni / variabili con nomi più appropriati e cambiare manualmente tutto include, scrive funzioni lunghe perché quando cambio automaticamente gli attributi di una funzione che riguarda la std lib Qt Creator sta cancellando i template così evito di farlo, e molti altri ..

Questo è abbastanza frustrante .. Cosa mi puoi consigliare? Devo essere più rilassato nelle "regole" della scrittura di codice pulito? Devo non pensare a questa roba e pensare solo a far funzionare le cose? Oppure (forse questa è la domanda più importata) devo pensare a qualche altro modo di lavorare? Non più modo incrementale / iterativo / agile, ma più cascata e dedicare più tempo alla progettazione di roba prima della codifica nella speranza di ridurre al minimo il refactoring quando i test sono passati?

Comunque, qualsiasi commento è benvenuto perché sono molto lontano dall'essere un buon programmatore:)

    
posta nkint 05.06.2013 - 17:12
fonte

2 risposte

13

TL; DR: C ++ ha un vecchio modello di compilazione e una sintassi hard-to-process (perché è altamente contestuale) che rende difficili gli strumenti, ma esistono. Il refactoring è un po 'più difficile da fare in C ++, ma è un po' più lento da fare rispetto ad altre lingue.

Penso che tu abbia bisogno di alcune informazioni aggiuntive sullo stesso C ++ e sugli strumenti correlati. Vediamo:

  1. Esistono strumenti di refactoring per C ++ che potrebbero aiutare a rinominare i file, ecc. Un esempio è il plug-in di Visual Studio Visual Assist X . Penso che Eclipse e altri editor abbiano anche alcuni strumenti di refactoring. Sono imperfetti, ma sono stati molto utili per me per anni. Ora, dove è meno utile è quando non organizzi il tuo codice in un modo che è normale e "forzato" in altri linguaggi come Java o Python o C # perché ...

  2. C ++ risente molto dell'utilizzo di un modello di compilazione molto vecchio. Rende più lento compilare una sorta di codice (intestazione-pesante), rende difficile individuare le intestazioni delle librerie, ecc. La compilazione deve essere capita per comprendere appieno perché, a seconda del tipo di libreria che utilizzi o sviluppi, non organizzerai le tue directory fonti allo stesso modo. Comunque, questo rende gli strumenti difficili da costruire attorno ad esso. In particolare perché le macro sono il modo in cui importiamo i moduli, il che significa che è tutto copia-incolla.

  3. La definizione di header + source è classica se si definiscono molti sistemi di classe, ma è ben lungi dall'essere il normale / unico modo di fare le cose in C ++. Ad esempio, molte Boost librerie C ++ sono solo di intestazione, il che significa che non c'è file cpp associato e non è possibile testare il codice senza aggiungere file cpp per compilarlo (le intestazioni non esistono realmente per il compilatore, vedere il modello di compilazione).

  4. Inoltre, la sintassi C ++ è altamente contestuale, il che significa che uno strumento che è in grado di ridefinire correttamente C ++ dovrebbe avere lo stesso tipo di informazioni sul codice sorgente elaborato come compilatore. Questa è una delle ragioni degli sforzi di LLVM per fornire librerie da analizzare e avere un modello corretto di codice C ++, che può quindi essere utilizzato dalle implementazioni degli strumenti. Tuttavia, solo pochi strumenti finora sono utilizzabili, ma ci aspettiamo che molti strumenti di refactoring emergano dallo sforzo di LLVM nei prossimi anni.

  5. Avere un sistema di moduli moderno, strutturato e ben definito come in Java, C #, Python e Ruby è chiaramente superiore per la velocità di compilazione, ma soprattutto per aiutare il compilatore a sapere cos'è un modulo e come i moduli sono collegati insieme. Il comitato standard ISO C ++ sta lavorando su un sistema di moduli così moderno da aggiungere nella versione C ++ 17 se è pronto allora. Il punto è che il problema è riconosciuto e in via di soluzione. Il lavoro su questo viene fatto nel compilatore LLVM / Clang .

  6. Fai attenzione che CMake è un sistema meta-build. Quello che intendo qui è che è indipendente dallo strumento di edizione che usi (io lo uso con Visual Studio per esempio) e aggiungo, in effetti, un'indirizzamento indiretto nel processo di aggiunta / rimozione / ridenominazione del file. Dato che C ++ non ha alcun sistema di generazione standard, è uno di questi strumenti, ma è una meta descrizione del tuo progetto. Ad esempio, se utilizzo direttamente i formati di progetto predefiniti di Visual Studio o QtCreator, è molto più semplice modificare i nomi dei file poiché è possibile farlo direttamente nell'editor. Ma in effetti questi file di progetto non sono compatibili con altri editor / piattaforme, motivo per cui si utilizza CMake per essere certi di essere in grado di generare file di progetto per una varietà di configurazioni di sviluppo. In sostanza, se non si utilizza CMake, il refactoring è più veloce, ma si perdono le funzionalità di cross-editor / cross-platform build-system.

Tutto ciò detto, la mia esperienza finora con il refactoring C ++ è che se hai qualcosa come VAssistX è sufficiente per fare la maggior parte di esso. Rinominare i file richiede più tempo per farlo correttamente rispetto ad altre lingue, ma non è così lungo. Detto questo, sarebbe utile se qualsiasi modifica a un progetto C ++ fosse più veloce da verificare, motivo per cui un sistema di moduli è davvero necessario. Si spera che non si lavori ancora con un codice che impiega un'ora o più per compilare, nel qual caso l'impatto sul refactoring è devastante in quanto le persone non vogliono modificare troppo il codice, perché li rende pazzi per dover aspettare. Questo è un problema difficile per la comunità C ++ e di nuovo è in corso il lavoro per risolverlo. Ma ci vorranno anni.

    
risposta data 05.06.2013 - 17:43
fonte
7

Now everything is more difficult mainly because there is no tools that can do it without cost. I bare myself in bad attitudes driven by lazyness: write several classes in a single super-long .h file because I don't want to create new files,

Lasciatemi riflettere su questo. Continua a spuntare e rimango sconcertato. Perché le persone sono ossessionate dal confezionamento di cose in file separati. Qui nel 2013? Perché mettere più di una classe in un'intestazione considerata pigra o cattiva?

E mentre per le intestazioni potrei trovare alcune scuse ma lo stesso vale anche per i file di implementazione. Chiedo perché e una risposta comune che temo sia "quindi trovo facilmente le mie cose". E la demo sembra convocare l'esploratore di soluzioni, trascorre un minuto chiudendo rami irrilevanti, pensa a un altro minuto che proietta, poi lo localizza su tre, all'interno cerca subbranches diversi, e alla fine cerca (usando eye) il file che corrisponde al nome della classe da un elenco di 50 nomi simili. Finalmente aprendolo con un piccolo urlo. Mentre voglio spararmi sei volte nella parte posteriore e saltare nel fiume.

Mentre abbiamo VA-X "file aperto nel progetto" e il modo migliore per "localizzare il simbolo", e la navigazione con 1 clic, o come ultima risorsa i file di ricerca vecchio stile, tutti possono produrre qualsiasi cosa da qualsiasi luogo in secondi. E anche con il metodo WTF appena descritto sarebbe molto più veloce trovare la cosa se tutti i 50 file fossero concatenati a uno solo.

Coloro che armeggiano con build con C ++ sanno bene che il tempo è abbastanza proporzionale con il numero di file .cpp mentre la lunghezza di un file è irrilevante. 50 classi inserite in 50 file si compilano 50 volte più lentamente di se le mettiamo tutte in un unico file. Ok, forse solo 25 volte, ma questa è ancora una cifra enorme.

Con editor davvero vecchi potrebbe essere un problema lavorare su più posti a meno che non si trovino in file separati. Ma posso chiedere un numero illimitato di finestre sullo stesso file, e in genere dividere qualsiasi finestra in più riquadri. E rilascia i segnalibri con un singolo tratto che vengono conservati fino all'eliminazione.

E a scopo di documentazione / lettura abbiamo doxygen - mostrerà le classi separatamente, con il codice inline rimosso, ecc. Indipendentemente da come le classi appaiono nelle fonti.

Anche se ho scelto di leggere direttamente la fonte, dal momento che una "classe" è un'unità di base? Era sempre "pacchetto", "modulo", "libreria" o qualcosa di simile che, avendo una filosofia concisa, e una discreta collezione di classi, funzioni che supportano enum, macros e altri gizmos. Migliore se tutto collocato piuttosto che diffuso dappertutto il posto.

Una serie di problemi riferiti nella domanda deriva in realtà da questa spaccatura artificiale. Le dichiarazioni devono essere mantenute nelle intestazioni? Quanti di questi sono effettivamente per "esportazione" e quanti per uso interno all'interno di un componente? La maggior parte di questi ultimi non è necessaria senza la divisione, in quanto la funzione sarà solo statica definita in anticipo. Ciò che è molto più leggero in termini di manutenzione.

L'unico svantaggio di join è che l'oggetto statico / at-namespace potrebbe scontrarsi. Ma quelli si spera siano nella lista nera molto tempo fa.

Che ne dici di refactoring del sistema verso uno stato che causa meno problemi? E non introdurre mal di testa artificialmente seguendo alcune idee lasciate da Noah?

    
risposta data 06.06.2013 - 02:52
fonte

Leggi altre domande sui tag