Prima di tutto, non confonderlo con la progettazione basata sui dati.
La mia comprensione del design orientato ai dati è che si tratta di organizzare i dati per un'elaborazione efficiente. Soprattutto per quanto riguarda i mancati cache ecc. Data Driven Design, d'altra parte, consente al controllo dei dati di gran parte del comportamento dei programmi (descritto molto bene dalla risposta di Andrew Keith ).
Supponi di avere oggetti a sfera nella tua applicazione con proprietà come colore, raggio, rimbalzo, posizione ecc.
Approccio orientato agli oggetti
In OOP descriveresti palle come questa:
class Ball {
Point position;
Color color;
double radius;
void draw();
};
E poi creeresti una raccolta di palline come questa:
vector<Ball> balls;
Approccio orientato ai dati
Nella progettazione orientata ai dati, tuttavia, è più probabile che tu scriva il codice in questo modo:
class Balls {
vector<Point> position;
vector<Color> color;
vector<double> radius;
void draw();
};
Come puoi vedere, non esiste più una singola unità che rappresenti più una palla. Gli oggetti palla esistono solo implicitamente.
Questo può avere molti vantaggi, dal punto di vista delle prestazioni. Di solito vogliamo fare operazioni su molte palline contemporaneamente. L'hardware di solito richiede grandi blocchi continui di memoria per funzionare in modo efficiente.
In secondo luogo, è possibile eseguire operazioni che riguardano solo una parte delle proprietà delle sfere. Ad esempio, se si combinano i colori di tutte le sfere in vari modi, si desidera che la cache contenga solo informazioni sul colore. Tuttavia, quando tutte le proprietà della palla sono memorizzate in un'unità, tirerai anche tutte le altre proprietà di una palla. Anche se non ne hai bisogno.
Esempio di utilizzo della cache
Supponiamo che ogni palla occupi 64 byte e che un Punto richieda 4 byte. Uno slot della cache richiede, diciamo, anche 64 byte. Se voglio aggiornare la posizione di 10 palline, devo inserire 10 * 64 = 640 byte di memoria nella cache e ottenere 10 mancate cache. Se comunque riesco a lavorare le posizioni delle sfere come unità separate, ci vorranno solo 4 * 10 = 40 byte. Si adatta a un recupero della cache. Quindi abbiamo solo 1 cache mancata per aggiornare tutte e 10 le palline. Questi numeri sono arbitrari - presumo che un blocco di cache sia più grande.
Ma illustra come il layout della memoria può avere un grave effetto sugli hit della cache e quindi sulle prestazioni. Ciò aumenterà di importanza solo con l'aumentare della differenza tra la velocità della CPU e quella della RAM.
Come impaginare la memoria
Nel mio esempio palla ho semplificato molto il problema, perché di solito per qualsiasi normale app si accederà probabilmente a più variabili insieme. Ad esempio, la posizione e il raggio verranno probabilmente usati insieme frequentemente. Quindi la tua struttura dovrebbe essere:
class Body {
Point position;
double radius;
};
class Balls {
vector<Body> bodies;
vector<Color> color;
void draw();
};
La ragione per cui dovresti fare questo è che se i dati usati insieme sono collocati in array separati, c'è il rischio che competano per gli stessi slot nella cache. In questo modo, caricando l'uno verrà eliminato l'altro.
Quindi, rispetto alla programmazione orientata agli oggetti, le classi che finisci per fare non sono correlate alle entità nel tuo modello mentale del problema. Poiché i dati sono raggruppati in base all'utilizzo dei dati, non avrai sempre nomi ragionevoli da dare alle tue classi in Design orientato ai dati.
Relazione con database relazionali
Il pensiero alla base del design orientato ai dati è molto simile a come si pensa ai database relazionali. L'ottimizzazione di un database relazionale può anche comportare l'utilizzo della cache in modo più efficiente, sebbene in questo caso la cache non sia cache della CPU ma pagine in memoria. Un buon progettista di database probabilmente dividerà i dati a cui si accede raramente in una tabella separata piuttosto che creare una tabella con un numero enorme di colonne dove solo alcune delle colonne sono mai state utilizzate. Potrebbe anche scegliere di denormalizzare alcune delle tabelle in modo che non sia necessario accedere ai dati da più posizioni sul disco. Proprio come con il design orientato ai dati, queste scelte vengono prese osservando quali sono i modelli di accesso ai dati e dove si trova il collo di bottiglia delle prestazioni.