Przede wszystkim nie myl tego z projektowaniem opartym na danych.
W moim rozumieniu Projektowanie zorientowane na dane polega na organizowaniu danych w celu wydajnego przetwarzania. Zwłaszcza jeśli chodzi o chybienia w pamięci podręcznej itp. Z drugiej strony, projektowanie oparte na danych polega na umożliwieniu danych kontrolowania wielu zachowań programów (bardzo dobrze opisane w odpowiedzi Andrew Keitha ).
Załóżmy, że w aplikacji znajdują się obiekty piłek z właściwościami takimi jak kolor, promień, sprężystość, położenie itp.
Podejście zorientowane obiektowo
W OOP opisałbyś kule w ten sposób:
class Ball {
Point position;
Color color;
double radius;
void draw();
};
Następnie stworzyłbyś kolekcję piłek w ten sposób:
vector<Ball> balls;
Podejście zorientowane na dane
Jednak w projektowaniu zorientowanym na dane jest bardziej prawdopodobne, że napiszesz kod w następujący sposób:
class Balls {
vector<Point> position;
vector<Color> color;
vector<double> radius;
void draw();
};
Jak widać, nie ma już pojedynczej jednostki reprezentującej jedną piłkę. Obiekty Ball istnieją tylko niejawnie.
Może to mieć wiele zalet, jeśli chodzi o wydajność. Zwykle chcemy wykonywać operacje na wielu piłkach jednocześnie. Sprzęt zwykle potrzebuje dużych ciągłych porcji pamięci, aby działać wydajnie.
Po drugie, możesz wykonywać operacje, które wpływają tylko na część właściwości piłki. Np. Jeśli na różne sposoby łączysz kolory wszystkich kulek, chcesz, aby twoja pamięć podręczna zawierała tylko informacje o kolorze. Jednak gdy wszystkie właściwości piłki są przechowywane w jednej jednostce, uwzględnisz również wszystkie inne właściwości piłki. Nawet jeśli ich nie potrzebujesz.
Przykład użycia pamięci podręcznej
Powiedzmy, że każda kula zajmuje 64 bajty, a punkt zajmuje 4 bajty. Również miejsce na pamięć podręczną zajmuje, powiedzmy, 64 bajty. Jeśli chcę zaktualizować pozycję 10 piłek, muszę wciągnąć 10 * 64 = 640 bajtów pamięci do pamięci podręcznej i uzyskać 10 braków pamięci podręcznej. Jeśli jednak mogę obrobić pozycje kulek jako oddzielne jednostki, zajmie to tylko 4 * 10 = 40 bajtów. To pasuje do jednego pobrania z pamięci podręcznej. W ten sposób otrzymujemy tylko 1 brak pamięci podręcznej, aby zaktualizować wszystkie 10 piłek. Te liczby są arbitralne - zakładam, że blok pamięci podręcznej jest większy.
Ale ilustruje, jak układ pamięci może mieć poważny wpływ na trafienia w pamięci podręcznej, a tym samym na wydajność. Będzie to rosło tylko w miarę zwiększania się różnicy między szybkością procesora i pamięci RAM.
Jak rozplanować pamięć
W moim przykładzie z piłką bardzo uprościłem problem, ponieważ zwykle w przypadku każdej normalnej aplikacji prawdopodobnie będziesz mieć dostęp do wielu zmiennych jednocześnie. Np. Pozycja i promień będą prawdopodobnie często używane razem. Wtedy twoja struktura powinna być:
class Body {
Point position;
double radius;
};
class Balls {
vector<Body> bodies;
vector<Color> color;
void draw();
};
Powód, dla którego należy to zrobić, jest taki, że jeśli dane używane razem są umieszczone w oddzielnych tablicach, istnieje ryzyko, że będą konkurować o te same gniazda w pamięci podręcznej. W ten sposób ładowanie jednego spowoduje wyrzucenie drugiego.
Zatem w porównaniu z programowaniem zorientowanym obiektowo, klasy, które ostatecznie tworzysz, nie są powiązane z bytami w twoim mentalnym modelu problemu. Ponieważ dane są grupowane na podstawie wykorzystania danych, nie zawsze będziesz mieć rozsądne nazwy, które nadasz swoim klasom w projektowaniu zorientowanym na dane.
Relacja z relacyjnymi bazami danych
Sposób myślenia dotyczący projektowania zorientowanego na dane jest bardzo podobny do tego, jak myślisz o relacyjnych bazach danych. Optymalizacja relacyjnej bazy danych może również obejmować bardziej wydajne wykorzystanie pamięci podręcznej, chociaż w tym przypadku pamięć podręczna nie jest pamięcią podręczną procesora, ale stronami w pamięci. Dobry projektant bazy danych prawdopodobnie podzieli rzadko używane dane na oddzielną tabelę, zamiast tworzyć tabelę z ogromną liczbą kolumn, gdy tylko kilka z nich jest kiedykolwiek używanych. Może również zdecydować się na denormalizację niektórych tabel, aby nie trzeba było uzyskiwać dostępu do danych z wielu lokalizacji na dysku. Podobnie jak w przypadku projektowania zorientowanego na dane, tych wyborów dokonuje się, patrząc na wzorce dostępu do danych i gdzie jest wąskie gardło wydajności.