En primer lugar, no confunda esto con el diseño basado en datos.
Comprendo que el diseño orientado a datos consiste en organizar sus datos para un procesamiento eficiente. Especialmente con respecto a las fallas de caché, etc. El diseño impulsado por datos, por otro lado, se trata de permitir que los datos controlen gran parte del comportamiento de sus programas (descrito muy bien por la respuesta de Andrew Keith ).
Supongamos que tiene objetos de bola en su aplicación con propiedades como color, radio, rebote, posición, etc.
Enfoque orientado a objetos
En OOP describirías bolas como esta:
class Ball {
Point position;
Color color;
double radius;
void draw();
};
Y luego crearías una colección de bolas como esta:
vector<Ball> balls;
Enfoque orientado a datos
Sin embargo, en el diseño orientado a datos, es más probable que escriba el código de esta manera:
class Balls {
vector<Point> position;
vector<Color> color;
vector<double> radius;
void draw();
};
Como puede ver, ya no hay una sola unidad que represente una Bola. Los objetos de bola solo existen implícitamente.
Esto puede tener muchas ventajas, en cuanto al rendimiento. Por lo general, queremos realizar operaciones en muchas bolas al mismo tiempo. El hardware generalmente quiere grandes porciones continuas de memoria para operar de manera eficiente.
En segundo lugar, puede realizar operaciones que afecten solo una parte de las propiedades de las bolas. Por ejemplo, si combina los colores de todas las bolas de varias maneras, entonces desea que su caché solo contenga información de color. Sin embargo, cuando todas las propiedades de la bola se almacenan en una unidad, también atraerá todas las demás propiedades de una bola. Aunque no los necesites.
Ejemplo de uso de caché
Digamos que cada bola ocupa 64 bytes y un punto toma 4 bytes. Una ranura de caché toma, digamos, 64 bytes también. Si quiero actualizar la posición de 10 bolas, tengo que extraer 10 * 64 = 640 bytes de memoria en el caché y obtener 10 errores de caché. Sin embargo, si puedo trabajar las posiciones de las bolas como unidades separadas, eso solo tomará 4 * 10 = 40 bytes. Eso cabe en una búsqueda de caché. Por lo tanto, solo obtenemos 1 falta de caché para actualizar las 10 bolas. Estos números son arbitrarios: supongo que un bloque de caché es más grande.
Pero ilustra cómo el diseño de la memoria puede tener un efecto severo en los éxitos de caché y, por lo tanto, en el rendimiento. Esto solo aumentará en importancia a medida que se amplíe la diferencia entre la velocidad de CPU y RAM.
Cómo diseñar la memoria
En mi ejemplo de bola simplifiqué mucho el problema, porque generalmente para cualquier aplicación normal es probable que accedan a múltiples variables juntas. Por ejemplo, la posición y el radio probablemente se usarán juntos con frecuencia. Entonces su estructura debería ser:
class Body {
Point position;
double radius;
};
class Balls {
vector<Body> bodies;
vector<Color> color;
void draw();
};
La razón por la que debe hacer esto es que si los datos utilizados juntos se colocan en matrices separadas, existe el riesgo de que compitan por los mismos espacios en la memoria caché. Así, cargar uno arrojará al otro.
Entonces, en comparación con la programación orientada a objetos, las clases que terminas haciendo no están relacionadas con las entidades en tu modelo mental del problema. Dado que los datos se agrupan en función del uso de datos, no siempre tendrá nombres razonables para dar a sus clases en Diseño orientado a datos.
Relación con bases de datos relacionales
El pensamiento detrás del diseño orientado a datos es muy similar a cómo piensa sobre las bases de datos relacionales. La optimización de una base de datos relacional también puede implicar el uso de la memoria caché de manera más eficiente, aunque en este caso, la memoria caché no es la memoria caché de la CPU sino páginas en la memoria. Un buen diseñador de bases de datos también probablemente dividirá los datos a los que se accede con poca frecuencia en una tabla separada en lugar de crear una tabla con una gran cantidad de columnas, donde solo se utilizan algunas de las columnas. También podría elegir desnormalizar algunas de las tablas para que no se tenga que acceder a los datos desde múltiples ubicaciones en el disco. Al igual que con el diseño orientado a datos, estas elecciones se realizan al observar cuáles son los patrones de acceso a datos y dónde está el cuello de botella en el rendimiento.