Desacoplamiento
En última instancia, se trata de desacoplarme al final del día en el nivel de diseño más fundamental sin los matices de las características de nuestros compiladores y enlazadores. Quiero decir que puede hacer cosas como hacer que cada encabezado defina solo una clase, use pimpls, envíe declaraciones a tipos que solo necesitan ser declarados, no definidos, tal vez incluso use encabezados que solo contengan declaraciones directas (ej .:) <iosfwd>
, un encabezado por archivo fuente , organizar el sistema de manera coherente en función del tipo de cosa que se declara / define, etc.
Técnicas para reducir las "dependencias en tiempo de compilación"
Y algunas de las técnicas pueden ayudar un poco, pero puede encontrarse agotando estas prácticas y aún así encontrar que su archivo fuente promedio en su sistema necesita un preámbulo de dos páginas de #include
directivas para hacer algo levemente significativo con tiempos de compilación disparados si se enfoca demasiado en reducir las dependencias de tiempo de compilación en el nivel de encabezado sin reducir las dependencias lógicas en los diseños de su interfaz, y aunque eso no puede considerarse "encabezados de espagueti" estrictamente hablando, yo Todavía diría que se traduce en problemas perjudiciales similares a la productividad en la práctica. Al final del día, si sus unidades de compilación todavía requieren una gran cantidad de información para ser visible para hacer algo, entonces se traducirá en un aumento de los tiempos de construcción y multiplicará las razones por las que potencialmente tiene que regresar y cambiar las cosas mientras hace que los desarrolladores sienten que están golpeando el sistema simplemente tratando de terminar su codificación diaria. Eso'
Puede, por ejemplo, hacer que cada subsistema proporcione una interfaz y un archivo de encabezado muy abstracto. Pero si los subsistemas no están desacoplados entre sí, entonces obtienes algo parecido al espagueti nuevamente con interfaces de subsistema que dependen de otras interfaces de subsistema con un gráfico de dependencia que parece un desastre para funcionar.
Declaraciones directas a tipos externos
De todas las técnicas que agoté para tratar de obtener una antigua base de código que tardó dos horas en compilarse, mientras que los desarrolladores a veces esperaban 2 días para su turno en CI en nuestros servidores de compilación (casi puedes imaginar esas máquinas de compilación como bestias de carga agotadas intentando frenéticamente mantenerse al día y fallar mientras los desarrolladores empujan sus cambios), lo más cuestionable para mí fue declarar los tipos definidos en otros encabezados. Y logré reducir esa base de código a 40 minutos más o menos después de años de hacer esto en pequeños pasos incrementales mientras intentaba reducir el "espagueti de encabezado", la práctica más cuestionable en retrospectiva (como en hacerme perder de vista la naturaleza fundamental de el diseño mientras el túnel se visualizaba en las interdependencias de los encabezados) declaraba los tipos definidos en otros encabezados.
Si imagina un Foo.hpp
encabezado que tiene algo como:
#include "Bar.hpp"
Y solo utiliza Bar
en el encabezado una forma que requiere declaración, no definición. entonces puede parecer una obviedad declarar class Bar;
para evitar hacer Bar
visible la definición de encabezado. Excepto en la práctica, a menudo encontrará que la mayoría de las unidades de compilación que usan Foo.hpp
todavía necesitan Bar
definirse de todos modos con la carga adicional de tener que incluirse Bar.hpp
encima Foo.hpp
, o se encuentra con otro escenario donde eso realmente ayuda y 99 El% de sus unidades de compilación puede funcionar sin incluir Bar.hpp
, excepto que plantea la pregunta de diseño más fundamental (o al menos creo que debería ser hoy en día) de por qué necesitan ver la declaración Bar
y por quéFoo
incluso debe molestarse en saberlo si es irrelevante para la mayoría de los casos de uso (¿por qué cargar un diseño con dependencias a otro que casi nunca se usa?).
Debido a que conceptualmente no hemos desacoplado Foo
de Bar
. Acabamos de hacerlo para que el encabezado de Foo
no necesite tanta información sobre el encabezado de Bar
, y eso no es tan sustancial como un diseño que realmente los hace completamente independientes uno del otro.
Scripting Embebido
Esto es realmente para bases de código de mayor escala, pero otra técnica que encuentro inmensamente útil es usar un lenguaje de script integrado para al menos las partes de más alto nivel de su sistema. Descubrí que podía incrustar Lua en un día y que podía llamar de manera uniforme a todos los comandos de nuestro sistema (afortunadamente, los comandos eran abstractos). Desafortunadamente, me encontré con un obstáculo en el que los desarrolladores desconfiaban de la introducción de otro idioma y, quizás lo más extraño, con el rendimiento como su mayor sospecha. Sin embargo, aunque podría entender otras preocupaciones, el rendimiento no debería ser un problema si solo utilizamos el script para invocar comandos cuando los usuarios hacen clic en botones, por ejemplo, que no realizan bucles fuertes propios (qué estamos tratando de hacer, preocuparse por las diferencias de nanosegundos en los tiempos de respuesta para un clic de botón?).
Ejemplo
Mientras tanto, la forma más efectiva que he presenciado después de agotar las técnicas para reducir los tiempos de compilación en grandes bases de código son arquitecturas que realmente reducen la cantidad de información requerida para que funcione cualquier cosa en el sistema, no solo desacoplar un encabezado de otro de un compilador perspectiva, pero exigiendo a los usuarios de estas interfaces que hagan lo que tienen que hacer mientras conocen (tanto desde el punto de vista humano como del compilador, un desacoplamiento verdadero que va más allá de las dependencias del compilador) lo mínimo.
El ECS es solo un ejemplo (y no estoy sugiriendo que use uno), pero al encontrarlo me demostró que puede tener algunas bases de código realmente épicas que aún se construyen sorprendentemente rápido mientras utilizan felizmente plantillas y muchas otras ventajas porque el ECS, por naturaleza, crea una arquitectura muy desacoplada donde los sistemas solo necesitan saber sobre la base de datos de ECS y, por lo general, solo un puñado de tipos de componentes (a veces solo uno) para hacer lo suyo:
Diseño, Diseño, Diseño
Y este tipo de diseños arquitectónicos desacoplados a nivel conceptual humano es más efectivo en términos de minimizar los tiempos de compilación que cualquiera de las técnicas que exploré anteriormente a medida que su base de código crece y crece y crece, porque ese crecimiento no se traduce en su promedio la unidad de compilación multiplica la cantidad de información requerida en la compilación y los tiempos de enlace para trabajar (cualquier sistema que requiera que su desarrollador promedio incluya una gran cantidad de cosas para hacer cualquier cosa también los requiere y no solo el compilador debe saber sobre una gran cantidad de información para hacer cualquier cosa ) También tiene más beneficios que tiempos de construcción reducidos y encabezados desenredados, ya que también significa que sus desarrolladores no necesitan saber mucho sobre el sistema más allá de lo que se requiere de inmediato para hacer algo con él.
Si, por ejemplo, puede contratar a un desarrollador de física experto para desarrollar un motor de física para su juego AAA que abarque millones de LOC, y puede comenzar muy rápidamente mientras conoce la información mínima absoluta en cuanto a tipos e interfaces disponibles así como los conceptos de su sistema, entonces eso naturalmente se traducirá en una cantidad reducida de información para que él y el compilador requieran construir su motor de física, y también se traducirá en una gran reducción en los tiempos de construcción, mientras que generalmente implica que no hay nada parecido a los espaguetis en cualquier parte del sistema. Y eso es lo que sugiero que priorice por encima de todas estas otras técnicas: cómo diseña sus sistemas. Agotar otras técnicas será la guinda en la parte superior si lo haces mientras que, de lo contrario,