Disaccoppiamento
Alla fine si tratta di disaccoppiarmi alla fine della giornata al livello di progettazione più fondamentale privo della sfumatura delle caratteristiche dei nostri compilatori e linker. Voglio dire che puoi fare cose come fare in modo che ogni intestazione definisca solo una classe, usare i pimpl, inoltrare dichiarazioni ai tipi che devono solo essere dichiarati, non definiti, forse anche usare intestazioni che contengono solo dichiarazioni in avanti (es:), <iosfwd>
un'intestazione per file sorgente , organizzare il sistema in modo coerente in base al tipo di oggetto dichiarato / definito, ecc.
Tecniche per ridurre le "dipendenze in fase di compilazione"
E alcune delle tecniche possono aiutare un po 'ma puoi ritrovarti a esaurire queste pratiche e trovare ancora il tuo file sorgente medio nel tuo sistema richiede un preambolo di due pagine di #include
direttive per fare qualcosa di leggermente significativo con tempi di costruzione alle stelle se ti concentri troppo sulla riduzione delle dipendenze in fase di compilazione a livello di intestazione senza ridurre le dipendenze logiche nei tuoi progetti di interfaccia e mentre ciò potrebbe non essere considerato "intestazioni di spaghetti" in senso stretto, io direi ancora che si traduce in problemi dannosi simili alla produttività nella pratica. Alla fine della giornata se le tue unità di compilazione richiedono ancora un carico di informazioni per essere visibili per fare qualsiasi cosa, allora si tradurrà in un aumento dei tempi di costruzione e moltiplicherà i motivi per cui devi potenzialmente tornare indietro e cambiare le cose mentre fai sviluppatori sembra che stiano testando il sistema solo cercando di terminare la loro codifica giornaliera. E'
Ad esempio, è possibile fare in modo che ciascun sottosistema fornisca un'interfaccia e un file di intestazione molto astratti. Ma se i sottosistemi non sono disaccoppiati l'uno dall'altro, si ottiene qualcosa che assomiglia di nuovo agli spaghetti con le interfacce del sottosistema a seconda delle altre interfacce del sottosistema con un grafico delle dipendenze che sembra un disastro per funzionare.
Dichiarazioni inoltrate a tipi esterni
Di tutte le tecniche che ho esaurito per cercare di ottenere un ex codebase che richiedeva due ore per essere costruito mentre gli sviluppatori a volte aspettavano 2 giorni per il loro turno in CI sui nostri server di build (puoi quasi immaginare quelle macchine di build come bestie esauste di carico che cercano freneticamente per tenere il passo e fallire mentre gli sviluppatori spingono le loro modifiche), il più discutibile per me era inoltrare i tipi definiti in altre intestazioni. E sono riuscito a portare quel codice a 40 minuti o giù di lì dopo anni di tempo in piccoli passi incrementali mentre cercavo di ridurre gli "spaghetti all'intestazione", la pratica più discutibile col senno di poi (come nel farmi perdere di vista la natura fondamentale di progettare mentre il tunnel visionava le interdipendenze delle intestazioni) veniva dichiarato in avanti tipi definiti in altre intestazioni.
Se immagini Foo.hpp
un'intestazione con qualcosa di simile:
#include "Bar.hpp"
E utilizza solo Bar
nell'intestazione un modo che richiede la sua dichiarazione, non la definizione. allora potrebbe sembrare un gioco da ragazzi dichiarare class Bar;
per evitare di rendere Bar
visibile la definizione nell'intestazione. Tranne che nella pratica spesso troverai che la maggior parte delle unità di compilazione che usano Foo.hpp
finiscono Bar
per essere necessariamente definite in ogni caso con l'onere aggiuntivo di doversi includere Bar.hpp
sopra Foo.hpp
, o ti imbatti in un altro scenario in cui ciò aiuta davvero e 99 La% delle tue unità di compilazione può funzionare senza includere Bar.hpp
, tranne per il fatto che solleva la domanda di progettazione più fondamentale (o almeno penso che dovrebbe al giorno d'oggi) del motivo per cui devono anche vedere la dichiarazione Bar
e perchéFoo
ha anche bisogno di preoccuparsi di saperlo se è irrilevante per la maggior parte dei casi d'uso (perché caricare un progetto con dipendenze rispetto a un altro a malapena usato?).
Perché concettualmente non abbiamo realmente disaccoppiato Foo
da Bar
. Abbiamo appena fatto in modo l'intestazione del Foo
non ha bisogno di quante più informazioni circa l'intestazione di Bar
, e che non è quasi come sostanziale come un disegno che rende veramente questi due completamente indipendenti l'uno dall'altro.
Scripting incorporato
Questo è davvero per basi di codice su larga scala, ma un'altra tecnica che trovo immensamente utile è quella di utilizzare un linguaggio di scripting incorporato almeno per le parti più di alto livello del sistema. Ho scoperto che sono stato in grado di incorporare Lua in un giorno e averlo in modo uniforme in grado di chiamare tutti i comandi nel nostro sistema (i comandi erano astratti, per fortuna). Sfortunatamente mi sono imbattuto in un blocco stradale in cui gli sviluppatori non hanno diffidato dell'introduzione di un'altra lingua e, forse più stranamente, con prestazioni come il loro più grande sospetto. Tuttavia, mentre potrei capire altre preoccupazioni, le prestazioni dovrebbero essere un problema se utilizziamo lo script solo per invocare comandi quando gli utenti fanno clic sui pulsanti, ad esempio, che non eseguono loop propri (cosa stiamo cercando di fare, preoccuparsi delle differenze di nanosecondi nei tempi di risposta per un clic sul pulsante?).
Esempio
Nel frattempo il modo più efficace a cui abbia mai assistito dopo aver esaurito le tecniche per ridurre i tempi di compilazione in grandi codebase sono architetture che riducono realmente la quantità di informazioni necessarie per far funzionare qualsiasi cosa nel sistema, non solo disaccoppiando un'intestazione da un'altra da un compilatore prospettiva ma che richiede agli utenti di queste interfacce di fare ciò che devono fare pur conoscendo (sia dal punto di vista umano che del compilatore, il vero disaccoppiamento che va oltre le dipendenze del compilatore) il minimo indispensabile.
L'ECS è solo un esempio (e non sto suggerendo di usarne uno), ma incontrarlo mi ha mostrato che puoi avere alcune basi di codice davvero epiche che si costruiscono ancora sorprendentemente rapidamente mentre usi felicemente modelli e molte altre chicche perché l'ECS, di nature, crea un'architettura molto disaccoppiata in cui i sistemi devono solo conoscere il database ECS e in genere solo una manciata di tipi di componenti (a volte solo uno) per fare le loro cose:
Design, design, design
E questo tipo di progetti architettonici disaccoppiati a livello umano e concettuale è più efficace in termini di minimizzazione dei tempi di compilazione rispetto a qualsiasi delle tecniche che ho esplorato sopra mentre la tua base di codice cresce e cresce e cresce, perché quella crescita non si traduce nella tua media unità di compilazione che moltiplica la quantità di informazioni richieste al momento della compilazione e dei tempi di collegamento per funzionare (qualsiasi sistema che richiede allo sviluppatore medio di includere un carico di roba per fare qualsiasi cosa richiede anche loro e non solo il compilatore di conoscere una grande quantità di informazioni per fare qualsiasi cosa ). Ha anche più vantaggi di tempi di costruzione ridotti e districare le intestazioni, poiché significa anche che i tuoi sviluppatori non hanno bisogno di sapere molto sul sistema oltre a ciò che è immediatamente necessario per fare qualcosa con esso.
Se, ad esempio, puoi assumere uno sviluppatore di fisica esperto per sviluppare un motore fisico per il tuo gioco AAA che si estende su milioni di LOC, e può iniziare molto rapidamente conoscendo le informazioni minime e nulle assoluto per quanto riguarda i tipi e le interfacce disponibili così come i concetti del tuo sistema, allora questo si tradurrà naturalmente in una quantità ridotta di informazioni che sia lui che il compilatore dovranno richiedere per costruire il suo motore fisico, e allo stesso modo si tradurranno in una grande riduzione dei tempi di costruzione, implicando in generale che non c'è niente di simile agli spaghetti ovunque nel sistema. Ed è quello che sto suggerendo di dare priorità a tutte queste altre tecniche: come progettate i vostri sistemi. Lo scarico di altre tecniche sarà la ciliegina sulla torta se lo fai mentre, altrimenti,