If I keep on writing more code then there will be a time when it will be difficult for me to organize the code.
Questo è il tuo problema: ottieni l'organizzazione giusta e lo stile dovrebbe fluire più facilmente.
Non aspetta per organizzare il tuo codice: mantieni il codice organizzato mentre procedi. Sebbene il linguaggio non lo faccia per te, il codice dovrebbe comunque essere organizzato in moduli con basso accoppiamento e alta coesione.
Questi moduli forniscono naturalmente uno spazio dei nomi. Abbrevia il nome del modulo (se è lungo) e prefisso i nomi delle funzioni con il loro modulo per evitare collisioni.
A livello dei singoli identificatori, questi sono all'incirca in ordine crescente di soggettività:
- scegliere una convenzione e attenervisi
- ad esempio,
function_like_this(struct TypeLikeThis variable)
è comune
-
Evita decisamente la notazione ungherese (sorry JNL)
-
a meno che tu non voglia utilizzarlo come originariamente previsto, il che significa che la notazione app di Simonyi piuttosto che la terribile versione di sistema
Perché? Potrei scrivere un saggio su questo argomento, ma ti suggerisco invece di leggere questo articolo di Joel Spolsky, e poi cacciare ancora un po 'se sei interessato. C'è un link alla carta originale di Simonyi in fondo.
-
evita typedefs del puntatore a meno che non siano tipi di cookie veramente opachi - confondono solo le cose
struct Type *ok;
typedef struct Type *TypePtr;
TypePtr yuck;
Che cosa intendo con un tipo di cookie opaco ? Intendo qualcosa usato all'interno di un modulo (o libreria, o qualsiasi altra cosa) che deve essere passato al codice client, ma quel codice client non può usare direttamente. Lo restituisce semplicemente alla biblioteca.
Ad esempio, una libreria di database potrebbe esporre un'interfaccia come
/* Lots of buffering, IPC and metadata magic held in here.
No, you don't get to look inside. */
struct DBContextT;
/* In fact, you only ever get a pointer, so let's give it a nice name */
typedef struct DBContexT *DBContext;
DBContext db_allocate_context(/*maybe some optional flags?*/);
void db_release_context(DBContext);
int db_connect(DBContext, const char *connect);
int db_disconnect(DBContext);
int db_execute(DBContext, const char *sql);
Ora, il contesto è opaco al codice cliente, perché non puoi guardare dentro. Devi solo restituirlo alla biblioteca. Qualcosa come FILE
è anche opaco, e un descrittore di file intero è anche un cookie , ma non è opaco.
Una nota sul design
Ho usato la frase basso accoppiamento e alta coesione sopra senza spiegazione, e mi sento un po 'male a riguardo. Puoi cercarlo e probabilmente trovare dei buoni risultati, ma proverò ad affrontarlo brevemente (di nuovo, potrei scrivere un tema ma cercherò di non farlo).
La libreria DB disegnata sopra mostra basso accoppiamento perché espone una piccola interfaccia al mondo esterno. Nascondendo i dettagli dell'implementazione (in parte con il trucco opaco dei cookie), impedisce al codice client di dipendere da questi dettagli.
Immaginate invece del cookie opaco, dichiariamo la struttura di contesto in modo che il suo contenuto sia visibile e che includa un descrittore di file socket per una connessione TCP al database. Se successivamente modifichiamo l'implementazione per supportare l'utilizzo di un segmento di memoria condivisa quando il DB è in esecuzione sulla stessa macchina, il client deve essere ricompilato anziché semplicemente ricollegato. Ancora peggio, il client potrebbe aver avviato utilizzando il descrittore di file, ad esempio chiamando setsockopt
per modificare la dimensione del buffer predefinita, e ora ha anche bisogno di una modifica del codice. Tutti questi dettagli dovrebbero essere nascosti all'interno del nostro modulo dove sono pratici, e questo dà un basso accoppiamento tra moduli.
L'esempio mostra anche alta coesione , in quanto tutti i metodi nel modulo riguardano la stessa attività (accesso al DB). Ciò significa che solo il codice che ha bisogno di per conoscere i dettagli dell'implementazione (cioè il contenuto del nostro cookie) ha effettivamente accesso ad essi, il che semplifica il debugging.
Puoi anche vedere che avere un singolo problema semplifica la scelta di un prefisso per raggruppare insieme queste funzioni.
Ora, dire che questo esempio è buono è facile (soprattutto perché non è nemmeno completo), ma non ti aiuta immediatamente. Il trucco è guardare, mentre scrivi ed estendi il tuo codice, per funzioni che fanno cose simili o che operano sugli stessi tipi (che potrebbero essere candidati per il proprio modulo), e anche per funzioni che fanno molte cose separate che non sono " Sono davvero correlati e potrebbero essere candidati alla separazione.