Post by PaulAls iemand met een professionele achtergrond in datamodellering die nu
Het lijkt me dat concepten als entiteit en object gerelateerd aan
elkaar zijn. Toch bestaan binnen een ERD veel many-to-many relaties
die opgelost worden middels een koppeltabel. Vaak zijn koppeltabellen
betekenisloos: zuivere constructies. Iets dergelijks vind ik (bijna)
niet terug in conceptuele modellen/class diagrammen. Daar lijken (?)
er veelal bestaande zaken gemodelleerd te worden.
Ik weet dat binnen OO verschillende mogelijkheden bestaan om M:N
1. Associatieklasse
2. Uitwerking middels "koppelklasse"; vergelijkbaar met een
koppelentiteit.
Die twee oplossingen zie ik eigenlijk nooit toegepast worden in OO.
Ze komen een beetje te veel ERD-achtig op me over.
Waar ERD informatie centraal in een tabel opslaat, slaat OO vaak
de informatie verspreid over de objecten op. Dat is een heel
andere aanpak, waardoor de oplossingen ook zeer verschillend zullen
zijn.
Als je OO wilt leren denken, zul je een aantal ERD reflexen moeten
afleren.
Post by Paul3. Toepassen van dimensiereductie; formeel is de relatie M:N, maar men
beperkt de zichtbaarheid tot 1:N.
Tja, dat kan natuurlijk altijd. Maar de OO oplossing voor M:N heeft
zoveel voordelen, dat zelfs de 1:N op die manier worden aangepakt.
Dus dit lost eigenlijk ook niets op.
Post by PaulAlle voorbeelden waar ik de hand op weet te leggen zijn echter niet
duidelijk in het M:N stuk.
Wie kan meer duidelijkheid geven in de relatie ERD en OO?
Laten we een stapje terug doen. Bij een M:N relatie heb je twee
kanten op een 1:N relatie die consistent met elkaar moeten zijn.
Want als A een relatie heeft met B, moet B dus ook de inverse relatie
met A hebben.
Bij tabellen is het niet mogelijk om een onbekend aantal waarden in
een row op te nemen. Bij objecten is het wel goed mogelijk om een
container (dynamische array of lijst of iets dergelijks) in het
object op te nemen.
Bij ERD moet je de informatie dus sowieso al in een aparte tabel zetten.
Bij M:N zouden dat twee tabellen worden, maar die zouden toch indentiek
worden, dus wordt het een tabel met twee indices. Dit heeft als voordeel
dat de informatie maar op een enkele plaats staat, en er dus geen
consistentie problemen kunnen ontstaan.
Bij OO kun je de informatie wel binnen het object zelf opslaan.
Wel moet je opletten op de consistentie. Gelukkig is het bij OO
zeer gebruikelijk om stukken code te laten uitvoeren als je
data update. Dus bij OO wordt meestal gekozen om de updates
via een aparte procedure te laten verlopen die beide relaties
tegelijk aanpast, zodat consistentie gegarandeerd is.
Het voordeel van die aanpak is dat je niet in een aparte structuur
hoeft op te zoeken welke objecten gerelateerd zijn, maar dat je
gewoon referenties in een containers in het object zelf opslaat.
Een nadeel is dat als door een bug de consistentie fout loopt,
je dat pas op een heel andere plek zult merken en het debuggen
dus vervelend zal worden. Unit-tests, om de relatie-beheer-code
apart te testen, kunnen hierbij helpen.
Een simpel voorbeeld (pseudo-code). Je hebt een klasse Vriend
met daarin een container Vrienden met nul of meer referenties
naar een Vriend. Deze container is 'private', dus alleen
toegankelijk voor procedures van de klasse Vriend.
procedure Vriend.NieuweVriendschap(Vriend B);
begin
self.Vrienden.Add(B);
B.Vrienden.Add(self);
end;
procedure Vriend.VerwijderVriendschap(Vriend B);
begin
self.Vrienden.Remove(B);
B.Vrienden.Remove(self);
end;
destructor Vriend.Destroy;
begin
for i in self.Vrienden do
begin
i.Vrienden.Remove(self);
end;
self.Vrienden.Clear; // Voor het geval dat...
end;
Zolang je verder nooit de container Vrienden aanpast, blijven
de relaties consistent. Je kunt natuurlijk wel de inhoud bekijken,
zoals met:
function Vriend.IsBevriendMet(Vriend B);
begin
return self.Vrienden.IsPresent(B);
end;
Op deze manier heb je altijd de relaties binnen handbereik, zonder
moeilijk te moeten doen ze in een andere tabel of structuur op te
zoeken. Dat komt de snelheid ten goede. Het gaat wel ten koste
van een beetje geheugen, maar wie maalt daar tegenwoordig nog om?
Deze aanpak heeft zo veel voordelen en zo weinig nadelen, dat
zelfs 1:N relaties vaak op deze manier worden aangepakt.
Voorbeeld: een werknemer heeft een enkele referentie naar zijn baas,
maar elke baas heeft een hele container met referenties naar
zijn ondergeschikten. In ERD moet je die inverse relatie
(baas->werknemer) steeds opnieuw weer opbouwen.
Hopelijk verduidelijkt dit een beetje hoe je met OO dit probleem
oplost. De oplossing is dus duidelijk anders dan bij ERD.
Als je een echt en complex voorbeeld wilt bekijken, kijk dan
naar Observer/Observable die vrij centraal staat binnen de
event-driven aanpak die vaak met OO gepaard gaat. Maar dat
zou voor een beginner misschien nog iets te complex kunnen
zijn.
Sjoerd Schreuder