[.NET] XmlSerializer en circulaire verwijzingen

Pagina: 1
Acties:
  • 384 views sinds 30-01-2008
  • Reageer

  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
Lang verhaal kort: hoe krijg ik de XmlSerializer zover dat hij een graaf van objecten serialiseert waarin circulaire verwijzingen voorkomen?

Ik ben op dit moment bezig met een .NET 1.1 project waarbij gebruik wordt gemaakt van een webservice om data tussen client en server uit te wisselen. Binnen de client en server heeft deze data de vorm van business-objecten (zoals Bedrijf, Medewerker, etc.) die referenties hebben naar elkaar. Zo zal een Bedrijf een collectie van Medewerkers hebben, maar elke Medewerker heeft ook een referentie naar het Bedrijf waar hij werkt.

Nu is dit geen probleem zolang deze objecten zich in memory bevinden, maar (je voelt 'm waarschijnlijk al aankomen) wanneer zo'n graaf van objecten geserialiseerd moet worden naar XML om door de webservice heen te kunnen sturen gaat het mis. De XmlSerializer ondersteunt namelijk niet het serialiseren van een graaf van objecten met cykels. D.w.z: als je Bedrijf B wilt serializen, en B heeft een referentie naar Medewerker M dan gaat het goed, maar wanneer M op zijn beurt terugverwijst naar B dan krijg je een circulaire verwijzing en dan weigert de XmlSerializer dienst met de melding "A circular reference was detected while serializing an object of type Bedrijf.".

Omdat er conceptueel gezien niets mis is met circulaire verwijzingen, en dit imo een tekortkoming is van de XmlSerializer, zou ik 'm toch graag zo ver willen krijgen dat hij een cyclic object graph kan (de)serialiseren.

Nu heb ik een aantal alternatieven overwogen, maar allemaal voelen ze niet lekker:
  1. De graaf van objecten preprocessen door dubbele objecten te vervangen door stubs (= een plaatsvervangend object met alleen het ID-veld van het originele object) alvorens deze aan de XmlSerializer te geven. Echter, een stub moet precies van hetzelfde type zijn als het originele object (het kan niet eens van een afgeleide klasse zijn), dus dat komt neer op elk business object een public property te geven met of het al dan niet een stub voorstelt, en de rest van zijn velden op null zetten. Niet erg elegant.
  2. De velden van de businessobjecten decoreren met attributen die omschrijven welke relatie de inverse voorstelt van de relatie die met dit attribuut is gedecoreerd. De object graph kan dan voor het serializen worden gepreprocessed door, uitgaande van het root-object dat verstuurd wordt, alleen de verwijzingen naar child objecten te behouden, en alle parent-verwijzingen op null te zetten. Tijdens het deserialisen kan dan de omgekeerde weg bewandeld worden.
    Echter, dit werkt alleen bij directe parent-child verwijzingen. Zo gauw er meer objecten in de cykel verwerkt zijn (A -> B -> C -> A) wordt het een puinhoop.
  3. Een eigen XmlSerializer schrijven die voor objecten die al eens geserialized zijn alleen een referentie (bijv. een ID veld) wegschrijft. Verreweg de meest flexibele oplossing, maar hoogstwaarschijnlijk veel code en testen. Nadeel is wel dat dit reflection-gebaseerd zal moeten zijn, terwijl de XmlSerializer gebruik maakt van on-the-fly gegenereerde en gecompileerde classes om dit te realiseren. Waarschijnlijk dus een performance penalty.
Wie heeft er tips en/of goede ideeen?

  • MTWZZ
  • Registratie: Mei 2000
  • Laatst online: 13-08-2021

MTWZZ

One life, live it!

Wel een beetje een long shot maar kun je je referenties niet in aparte elementen onderbrengen?
Dus voor de serialisatie de referenties uit je business objecten halen en in aparte elementen onderbrengen en pas daarna de boel serialiseren. En bij het de-serialiseren de boel dan weer omdraaien.

Nu met Land Rover Series 3 en Defender 90


  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

MrBucket schreef op donderdag 15 maart 2007 @ 21:28:
Omdat er conceptueel gezien niets mis is met circulaire verwijzingen, en dit imo een tekortkoming is van de XmlSerializer, zou ik 'm toch graag zo ver willen krijgen dat hij een cyclic object graph kan (de)serialiseren.
zoek maar eens op
"law of demeter"

Circulaire referenties zijn WEL fout in veruit de meeste gevallen.

Om je probleem op te lossen:
Is er geen attribuut dat aangeeft dat de Bedrijf parameter van een werknemer niet moet worden geserialized?

[ Voor 13% gewijzigd door H!GHGuY op 16-03-2007 23:55 ]

ASSUME makes an ASS out of U and ME


Verwijderd

Definieer "fout"... ;)
Neem bv. een Delphi Windows applicatie met een Form, een paar Panels en een handjevol Controls. Ieder control weet wie z'n Owner is ('t form) en wie z'n Parent is ('t panel waar 'ie opstaat), maar 't panel houdt ook een lijst met Controls bij die 'ie faciliteert en het form een lijst met Components die 'ie beheert.

Dit valt op zich prima te (de)serializen, Delphi's RTTI doet in feite niets anders, maar de .NET XmlSerializer kan hier niet mee overweg. Je kunt 't bij het serializen natuurlijk wel omzeilen door Parent en Owner op [Serializable(false)] te zetten, maar dan heb je een probleem bij het deserializen.
Je moet dan nl. die ontbrekende referenties zelf weer herbouwen. Soms is dat goed te doen, maar soms moet je je in allerlei reflection bochten wringen om 't voor elkaar te krijgen.

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

Verwijderd schreef op zaterdag 17 maart 2007 @ 00:45:
Definieer "fout"... ;)
Neem bv. een Delphi Windows applicatie met een Form, een paar Panels en een handjevol Controls. Ieder control weet wie z'n Owner is ('t form) en wie z'n Parent is ('t panel waar 'ie opstaat), maar 't panel houdt ook een lijst met Controls bij die 'ie faciliteert en het form een lijst met Components die 'ie beheert.

Dit valt op zich prima te (de)serializen, Delphi's RTTI doet in feite niets anders, maar de .NET XmlSerializer kan hier niet mee overweg. Je kunt 't bij het serializen natuurlijk wel omzeilen door Parent en Owner op [Serializable(false)] te zetten, maar dan heb je een probleem bij het deserializen.
Je moet dan nl. die ontbrekende referenties zelf weer herbouwen. Soms is dat goed te doen, maar soms moet je je in allerlei reflection bochten wringen om 't voor elkaar te krijgen.
Wie zegt dat het referenties/pointers zijn?
Zelfs al is het zo, volgende code doorbreekt de lus door invoering van een soort soft-reference (weak pointer of hoe je't ook wil noemen).

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Form
{
public:
  void AddControl(Control c) { m_Controls.Add(c); c.SetParent(this); }
  Handle GetHandle();
private:
  ControlContainer m_Controls;
}
class Control
{
public:
  void SetParent(Form f) { m_ParentHandle = f.GetHandle(); }
  Form GetParent() { return Form.FromHandle(m_ParentHandle);
private:
  Handle m_ParentHandle;
}


Kijk toch maar even die law of demeter na ;)

ASSUME makes an ASS out of U and ME


  • whoami
  • Registratie: December 2000
  • Laatst online: 00:54
H!GHGuY schreef op vrijdag 16 maart 2007 @ 23:51:
[...]


zoek maar eens op
"law of demeter"
:? Dat heeft imho helemaal niets met de law of demeter te maken.
Circulaire referenties zijn niet persé fout, maar als je ze kan vermijden maakt het je leven meestal heel wat makkelijker.

(Bv, een klant heeft orders; in dit geval kan je per order een referentie bijhouden naar de klant tot wie het order behoort. Je kan dan ook in je 'Klant' class een lijst bijhouden van Orders die die klant heeft, maar is dat wel nodig ? Je kan die orders ook gewoon ophalen als het nodig is door een method die je gewoon een OrderCollectie teruggeeft met daarin de orders van die klant, en je klant object hoeft die lijst niet bij te houden.
Circulaire referenties zijn WEL fout in veruit de meeste gevallen.
Kan je dit ook onderbouwen ?
Om je probleem op te lossen:
Is er geen attribuut dat aangeeft dat de Bedrijf parameter van een werknemer niet moet worden geserialized?
Er is zo'n attribuut (XmlIgnoreAttribute), maar, dan heeft hij aan z'n client kant wel een werknemer object met een lege bedrijf-property.

https://fgheysels.github.io/


  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
@MTWZZ:
Dat idee heb ik ook even overwogen, maar het vervelende is dat je XML dan niet echt meer 'human readable' is (en daarbij is het volgens mij nog aardig wat werk). Ik wil graag de XML-representatie zo dicht mogelijk bij de object-representatie houden.

@Afterlife en whoami:
Voor bidirectionele parent-child relaties (parent heeft een collectie van children, elk child heeft een referentie naar zijn parent) is het in theorie inderdaad mogelijk om maar 1 van beide relaties te serializeren, omdat je de inverse relatie dan weer kan afleiden tijdens het deserialiseren.

Naast het probleem dat dit erg lastig wordt bij grotere cirkels (A -> B -> C -> A),
is het ook nog zo dat ik de ene keer de parent relatie weg moet laten (omdat ik een Bedrijf serialiseer met een collectie van zijn medewerkers), terwijl ik de volgende keer de child-relaties weg moet laten (als ik een Medewerker serialiseer met het Bedrijf waar hij werkt).
En helaas kun je het XmlIgnoreAttribute niet run-time plaatsen of weghalen, al naar gelang wat de situatie vereist...

  • mulder
  • Registratie: Augustus 2001
  • Laatst online: 09:27

mulder

ik spuug op het trottoir

MrBucket schreef op donderdag 15 maart 2007 @ 21:28:
Een eigen XmlSerializer schrijven die voor objecten die al eens geserialized zijn alleen een referentie (bijv. een ID veld) wegschrijft. Verreweg de meest flexibele oplossing, maar hoogstwaarschijnlijk veel code en testen. Nadeel is wel dat dit reflection-gebaseerd zal moeten zijn, terwijl de XmlSerializer gebruik maakt van on-the-fly gegenereerde en gecompileerde classes om dit te realiseren. Waarschijnlijk dus een performance penalty.
Je zou elke object een eigen XmlSerializer kunnen geven en die dan aan elkaar plakken of idd en eigen serializer schrijven (wat imho niet perse met reflection hoeft; je voegt dan zelf de attributen toe, dit heeft het voordeel dat je makkelijker 'backwards compatible' kan zijn) En die performance zal wel los lopen toch?

oogjes open, snaveltjes dicht


  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
Wat me het mooiste lijkt is om als het ware net tussen de XmlSerializer en de te serialiseren objecten te gaan zitten. Dus dat ik bij elk object waarbij de XmlSerializer op het punt staat het te serialiseren, dat ik nog net even de kans krijg om te kiezen of het wel geserialiseerd moet worden, of dat ik ervoor kies in plaats daarvan mijn eigen XML (een stub-regeltje) weg te schrijven.

Zo hoef ik niet de XmlSerializer helemaal zelf opnieuw te implementeren, maar kan ik het proces wel zodanig sturen dat er geen circulaire referenties worden weggeschreven.

Ik zie hiervoor twee mogelijke ingangen:
  1. XmlSerializer.Serialize(object, XmlSerializationWriter) overriden. Echter, de XmlSerializationWriter class laat zich volgens mij niet makkelijk uitbreiden. Het heeft heel veel members, waarvan er erg weinig echt geimplementeerd zijn.
  2. Elk business object IXmlSerializable laten implementeren, zodat ik in de WriteXml() methode kan bepalen of ik de XmlSerializer het echte object moet laten wegschrijven of dat ik zelf alleen een stub wegschrijf. Dit kan volgens mij ook niet, omdat een object dat IXmlSerializable implementeert nooit meer door de XmlSerializer kan worden weggeschreven (omdat het die call dan altijd delegeert aan WriteXml() op mijn business object). Netto effect is dat ik dan alsnog het write-proces zelf moet schrijven.
Het liefst zie ik toch dat ik de XmlSerializer zo ver krijg om het leeuwendeel van het werk voor mij te doen, maar als het echt niet anders kan dan zal ik er toch zelf 1 moeten schrijven (ook als reactie op Don Facundo's post).

  • EfBe
  • Registratie: Januari 2000
  • Niet online
H!GHGuY schreef op vrijdag 16 maart 2007 @ 23:51:
[...]
Circulaire referenties zijn WEL fout in veruit de meeste gevallen.
Onzin, in een OO omgeving kan ieder object naar andere verwijzen. Waarom moet die reference dan eenzijdig worden gelegd? Immers, customer.Orders is indirecte reference naar orders die dan order.Customer hebben gezet naar die customer instance. Die references niet leggen kan je code complexer maken (of onmogelijk).
Om je probleem op te lossen:
Is er geen attribuut dat aangeeft dat de Bedrijf parameter van een werknemer niet moet worden geserialized?
Nee, circular references zijn niet op te lossen met die debiele XmlSerializer. Je moet dan IXmlSerializable implementeren en dan bv een lijst bijhouden van objects die je al gehad hebt en ipv nog eens het object als xml te serializen, serialize je een special element wat aangeeft welk object daar dan stond. Bij het deserializen bouw je die lijst dan weer op, en vervangt na afloop alle special tags door de objects die je al hebt ingelezen.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


Verwijderd

H!GHGuY schreef op zaterdag 17 maart 2007 @ 09:16:
Wie zegt dat het referenties/pointers zijn?
Als je refereert naar m'n Delphi voorbeeldje: de Delphi VCL source zelf:
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
TComponent = class(TPersistent, IInterface, IInterfaceComponentReference)
private
  FOwner: TComponent;
  //...
public
  property Owner: TComponent read FOwner;
  //...
end;


TControl = class(TComponent)
private
  FParent: TWinControl;
  //...
public
  property Parent: TWinControl read FParent write SetParent;
  //...
end;
Kijk toch maar even die law of demeter na ;)
Dit heeft niks met Law of Demeter te maken. "Don't talk to strangers", maar als je wilt dat beide kanten een conversatie kunnen beginnen, moeten beide kanten weten met wie ze wel mogen praten. Circular reference dus.

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

Oops, my mistake. Op zich heeft het er niet mee te maken, nee.

Je creert een strikte hierachie van dependencies waarbij je in de hierarchie enkel top-down gaat en niet bottom-up. Moet je toch even een parent(-pointer) object meegeven, dan moet dat tijdens de functie call en enkel gedurende de functie call.
Om toch problemen te vermijden waarbij dependencies circulair zijn is een weak pointer handig. (met weak pointer bedoel ik zowel een key-data systeem of echte weak pointers of andere systemen.)

Wie is verantwoordelijk over de relatie ?
Doe ik Form.Add(Control) of Control.SetParent(Form) ? Kunnen eventuele GC's (en ja de huidige GC's hebben er geen probleem mee) ermee overweg. Wat met bvb XML serialization ?

Gebruik een soort Handle en je probleem is opgelost.
Circulaire dependencies zijn pure gruwel in een goed design.

ASSUME makes an ASS out of U and ME


  • EfBe
  • Registratie: Januari 2000
  • Niet online
H!GHGuY schreef op zaterdag 17 maart 2007 @ 16:53:
Je creert een strikte hierachie van dependencies waarbij je in de hierarchie enkel top-down gaat en niet bottom-up.
Volgens mij snap je iets niet: er IS geen hierarchie, alleen references.

Als Foo een reference naar Bar heeft en omgekeerd, is er dan een hierarchy? Indien jij ja gaat roepen, hoe dan? Wie is de parent?

Verder is een enkele reference niet echt handig, want je IMPLICEERT nl. wel degelijk een dubbele reference alleen die kun je niet gebruiken, want je kunt niet tegen de reference in navigeren (order.Customer is er bv niet, maar die order zit wel in customer.Orders).

Waarom zou ik dan niet van order naar customer mogen? Omdat jij dat vindt?
Moet je toch even een parent(-pointer) object meegeven, dan moet dat tijdens de functie call en enkel gedurende de functie call.
Om toch problemen te vermijden waarbij dependencies circulair zijn is een weak pointer handig. (met weak pointer bedoel ik zowel een key-data systeem of echte weak pointers of andere systemen.)
Waarom zou mij dat nou moeten boeien? Dat zijn pure technische details die opgelost MOETEN zijn in je framework anders moet je een ander framework zoeken. Zo simpel is het gewoon. Ik ga echt geen reference collections aanleggen omdat er wellicht in het framework technisch gezien brak mee omgegaan wordt.
Wie is verantwoordelijk over de relatie ?
Doe ik Form.Add(Control) of Control.SetParent(Form) ? Kunnen eventuele GC's (en ja de huidige GC's hebben er geen probleem mee) ermee overweg. Wat met bvb XML serialization ?
Geloof me, wanneer een GC hier niet mee overweg kan is de GC dom, en dat daargelaten: waarom moet de developer zich daarover druk maken? Juist om dit soort dingen gebruik je een framework dat dit voor je regelt. Als dat framework met een GC komt die geen circular references kan oplossen, dan heb je een probleem want dan moet je je bezig gaan houden met plumbing van het framework terwijl je juist bezig wilt zijn met je EIGEN code.

XML serialization is uberhaupt een onbegrepen fenomeen. ik bedoel: XML serialization gaat over data, niet over objects. Dus al wat je wilt is de data in een graph in xml vorm en eventueel die data weer interpreteren en daar een graph van objects MET die data van opbouwen.

Wat je vaak ziet is dat de XML als data zowel de object data alsook de object graph structuur in zich verenigt. Dit bezorgt de problematiek eigenlijk.

Als je kijkt naar de SOAP serializer, dan zie je hoe je dit kunt oplossen. Deze SOAP serializer produceert ook XML van een object graph maar kan wel met circular references overweg. Dit doet hij door eerst gewoon alle objects onder elkaar te serializen ZONDER navigation. Daarna komt de graph met alleen reference ID's naar de objects in kwestie.
Circulaire dependencies zijn pure gruwel in een goed design.
Nee, ze zijn alleen een gruwel voor mensen die prutsen met inferieure frameworks. De limits van het framework zijn ook de limits van de complete werkruimte die je hebt en DAARDOOR moet je bij prutsframeworks ook veel doen op het gebied van plumbing en infrastructure.

Dat is wellicht leuk voor een zondagmiddag en leuk om uit te vinden hoe je bepaalde dingen kunt oplossen, maar als je je werk gewoon wilt afmaken dan wil je je hier niet mee bezig houden.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


Verwijderd

H!GHGuY schreef op zaterdag 17 maart 2007 @ 16:53:
Wie is verantwoordelijk over de relatie ?
Doe ik Form.Add(Control) of Control.SetParent(Form) ?
Bij Delphi is uiteindelijk het Form (de Owner) de baas: de constructor van het Control krijgt een Owner als parameter mee, deze roept de InsertComponent method van die Owner aan, welke op z'n beurt weer de Owner property van het control set.
Circulaire dependencies zijn pure gruwel in een goed design.
Dependencies wel, references absoluut niet.

  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
H!GHGuY schreef op zaterdag 17 maart 2007 @ 16:53:
Circulaire dependencies zijn pure gruwel in een goed design.
Misschien in sommige situaties wel, maar in mijn situatie in ieder geval niet.

Let wel: het gaat hier niet om C header files die elkaar willen includen, of een MVC-patroon waarin het model afhankelijk is van zijn view, maar om business-entiteiten die relaties met elkaar hebben. Het is niets anders dan een graaf, een datastructuur.

En net zoals je in een binaire boom, gelinkte lijst of skiplist jezelf niet druk maakt om de referenties die elke node naar de anderen heeft, zou je dat hier ook niet moeten doen.

  • whoami
  • Registratie: December 2000
  • Laatst online: 00:54
EfBe schreef op zaterdag 17 maart 2007 @ 12:40:
[...]
Nee, circular references zijn niet op te lossen met die debiele XmlSerializer.
Je kan met XmlIgnore aangeven dat een property niet geserialized moet worden; ofwel maak je van die property een getter only.
Maar dan zit je idd nog met het probleem van het deserializen, en het bijwerken van die referenties. Jammer genoeg heeft de XmlSerializer niet zoiets als het OnDeserialized attribute dat je met de Binary & SoapFormatter kunt gebruiken.
Ik heb net even met die IXmlSerializable lopen spelen, en het lukt me wel om een enkel object met referenties te serializeren / deserializeren, en de ref's terug goed te krijgen, maar als ik met een collectie ga werken, lukt het deserializen niet echt; enkel het eerste element wordt gedeserialized.

https://fgheysels.github.io/


  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

EfBe schreef op zaterdag 17 maart 2007 @ 19:10:
[...]

Volgens mij snap je iets niet: er IS geen hierarchie, alleen references.
Je kan bijna altijd een hierarchie in je objectstructuur stoppen. Wat bedoel jij met een reference?
Bedoel je een C-style pointer of een DB-like ID of nog iets anders ? Ik heb het over C-style pointers (of java-like object references).
Als Foo een reference naar Bar heeft en omgekeerd, is er dan een hierarchy? Indien jij ja gaat roepen, hoe dan? Wie is de parent?
Misschien moet Bar wel geen reference hebbn naar Foo om goed te werken.
Een customer _heeft_ orders maar een order _heeft_ geen customer. Hoogstens een vermelding wie de order heeft geplaatst. Natuurlijk kan je dat in code omzeilen door Order.GetCustomer te doen die ook effectief een Customer teruggeeft, maar imo haalt de netste onderliggende code van een ID de juiste customer op ipv een directe reference.
Verder is een enkele reference niet echt handig, want je IMPLICEERT nl. wel degelijk een dubbele reference alleen die kun je niet gebruiken, want je kunt niet tegen de reference in navigeren (order.Customer is er bv niet, maar die order zit wel in customer.Orders).

Waarom zou ik dan niet van order naar customer mogen? Omdat jij dat vindt?
zie hierboven
[...]

Waarom zou mij dat nou moeten boeien? Dat zijn pure technische details die opgelost MOETEN zijn in je framework anders moet je een ander framework zoeken. Zo simpel is het gewoon. Ik ga echt geen reference collections aanleggen omdat er wellicht in het framework technisch gezien brak mee omgegaan wordt.
Wat heeft dat nou met frameworks te maken?

C++:
1
2
3
4
5
6
7
void Order::Print(Printer& printer, Customer& cust)
{
  for_each (m_OrderLines.begin(), m_OrderLines.end(), PrintOrderline(printer))
  printer.Print(cust.Name);
  printer.Print(cust.Address);
  printer.Print(GetTotalPrice());
}

Dat is wat ik bedoel met: als je dan toch even de parent nodig hebt, dan geef je die mee tijdens een functie call en enkel dan.
[...]

Geloof me, wanneer een GC hier niet mee overweg kan is de GC dom, en dat daargelaten: waarom moet de developer zich daarover druk maken? Juist om dit soort dingen gebruik je een framework dat dit voor je regelt. Als dat framework met een GC komt die geen circular references kan oplossen, dan heb je een probleem want dan moet je je bezig gaan houden met plumbing van het framework terwijl je juist bezig wilt zijn met je EIGEN code.
Zoals ik ZELF al aangaf: de meeste GC's kunnen daarmee overweg. Ik _dacht_ echter dat in Small (programmeertaal) een GC zat die er niet mee overweg kon.
XML serialization is uberhaupt een onbegrepen fenomeen. ik bedoel: XML serialization gaat over data, niet over objects. Dus al wat je wilt is de data in een graph in xml vorm en eventueel die data weer interpreteren en daar een graph van objects MET die data van opbouwen.

Wat je vaak ziet is dat de XML als data zowel de object data alsook de object graph structuur in zich verenigt. Dit bezorgt de problematiek eigenlijk.

Als je kijkt naar de SOAP serializer, dan zie je hoe je dit kunt oplossen. Deze SOAP serializer produceert ook XML van een object graph maar kan wel met circular references overweg. Dit doet hij door eerst gewoon alle objects onder elkaar te serializen ZONDER navigation. Daarna komt de graph met alleen reference ID's naar de objects in kwestie.
Imo kun je maar best even afkijken van databases wanneer je serializet. Veel concepten van daar zijn, zoals je aangeeft met SOAP, redelijk weerspiegelbaar binnen XML en andere mogelijkheden.
[...]

Nee, ze zijn alleen een gruwel voor mensen die prutsen met inferieure frameworks. De limits van het framework zijn ook de limits van de complete werkruimte die je hebt en DAARDOOR moet je bij prutsframeworks ook veel doen op het gebied van plumbing en infrastructure.

Dat is wellicht leuk voor een zondagmiddag en leuk om uit te vinden hoe je bepaalde dingen kunt oplossen, maar als je je werk gewoon wilt afmaken dan wil je je hier niet mee bezig houden.
Hier begin je weer over frameworks. (Waarom?)
Niet iedere taal heeft frameworks. Soms moet je echt alles lekker zelf schrijven en dan kan ik het je op een blaadje geven: vermijd toch maar circular references waar mogelijk.

Dat jij met "superiere" frameworks werkt (proficiat daarvoor overigens) die virtueel alles voor jou doen (waar hou jij je dan dagelijks nog mee bezig eigenlijk aangezien je overbodig bent dan) is jouw zaak.
Denk ook even in andermans leefwereld waar niet alles vanzelfsprekend is. Dat jouw frameworks er nou net mee overweg kunnen is goed voor jou. Ik wed dat al jouw objecten parentreferences hebben zelfs al heb je ze niet nodig, OMDAT het kan.
Ik wil jou dan wel eens je frameworks afnemen en zien hoe je je zal ergeren op het slechte concept van backpointers.

Toen ik gisteren even de LoD opfriste om te zien dat die er eigenlijk niets mee te maken had kwam ik dit tegen:
http://www.builderau.com....028292,339180181-2,00.htm


En nu ik er nog even op google kom ik dit tegen:
http://smallwiki.unibe.ch/context/examples/

En op dit laatste staat eigenlijk dat LoD er wel mee te maken heeft.
Als jij backpointers implementeert dan ga je automatisch via die backpointer naar objecten waar je eigenlijk niet mee mag "praten". (Nu herinner ik me weer waarom ik die 2 samen in 1 presentatie-sessie gekregen heb. Ik had er enkel de conclusies even van onthouden.)
Dus eigenlijk staat mijn eerste comment in deze thread nog hard overeind.

Doe je dit niet dan ga jij waarschijnlijk methodes als:
Order.GetCustomerAddress en ook Order.ItemPrice(...)
implementeren.
MrBucket schreef op zaterdag 17 maart 2007 @ 19:37:
[...]

Misschien in sommige situaties wel, maar in mijn situatie in ieder geval niet.

Let wel: het gaat hier niet om C header files die elkaar willen includen, of een MVC-patroon waarin het model afhankelijk is van zijn view, maar om business-entiteiten die relaties met elkaar hebben. Het is niets anders dan een graaf, een datastructuur.

En net zoals je in een binaire boom, gelinkte lijst of skiplist jezelf niet druk maakt om de referenties die elke node naar de anderen heeft, zou je dat hier ook niet moeten doen.
Om die graaf te serializen ga je toch meestal een vertrekpunt hebben. Een soort root vanwaar alles vertrekt. Volgens mij is de ideale oplossing om conceptueel breadth-first door je graaf te lopen en enkel forward links hard op te slaan en de back en side-links op te slaan als soft-references.
Als je bovendien je objecten ook (zoals ik hierboven al aangaf) enkel die soft-references laat opslaan met daarbij de juiste methodes die wel het juiste object teruggeven adhv die soft-reference dan is je probleem imo opgelost.

ASSUME makes an ASS out of U and ME


  • EfBe
  • Registratie: Januari 2000
  • Niet online
H!GHGuY schreef op zondag 18 maart 2007 @ 10:42:
[...]
Je kan bijna altijd een hierarchie in je objectstructuur stoppen. Wat bedoel jij met een reference?
Bedoel je een C-style pointer of een DB-like ID of nog iets anders ? Ik heb het over C-style pointers (of java-like object references).
Een ID is semantisch, een pointer is een harde referentie. Dus ik had het over een pointer. Object A referenced object B.
Misschien moet Bar wel geen reference hebbn naar Foo om goed te werken.
Wat houdt 'goed' in in deze context? Let wel: als 'goed' hier inhoudt dat het dan werkt omdat de infrastructure code dan niet uit elkaar valt dan ben je niet goed bezig.
Een customer _heeft_ orders maar een order _heeft_ geen customer. Hoogstens een vermelding wie de order heeft geplaatst.
Een order heeft geen customer? Sorry maar ik moet daar toch echt hard om lachen. Verder is het een voorbeeld. Jij beknot jezelf met references want 'oh jee, circular references zijn slecht!!!111', terwijl je niet kunt aangeven waarom.
Natuurlijk kan je dat in code omzeilen door Order.GetCustomer te doen die ook effectief een Customer teruggeeft, maar imo haalt de netste onderliggende code van een ID de juiste customer op ipv een directe reference.
En wat als ik een in-memory object graph heb? Jij wilt die t.a.t. een DAG laten zijn, maar dat is natuurlijk onzin.
Wat heeft dat nou met frameworks te maken?
C++:
1
2
3
4
5
6
7
void Order::Print(Printer& printer, Customer& cust)
{
  for_each (m_OrderLines.begin(), m_OrderLines.end(), PrintOrderline(printer))
  printer.Print(cust.Name);
  printer.Print(cust.Address);
  printer.Print(GetTotalPrice());
}

Dat is wat ik bedoel met: als je dan toch even de parent nodig hebt, dan geef je die mee tijdens een functie call en enkel dan.
En hoe krijg jij die parent dan tevoorschijn? Je kunt immers niet naar die parent navigeren in jouw briljante code.

Sorry, maar ik vind wat je voorstelt maar onzinnig. Het levert immers geen ENKEL voordeel op, alleen nadelen: extra code, extra roundtrips naar persistent storage wellicht, complexere algoritmen...
Zoals ik ZELF al aangaf: de meeste GC's kunnen daarmee overweg. Ik _dacht_ echter dat in Small (programmeertaal) een GC zat die er niet mee overweg kon.
Dan is die GC een belemmering in de vrijheid die je hebt om software te maken, want je kunt dan dus iets NIET in die taal maken, bv een double-linked list. M.a.w.: een omgeving waar je niet veel aan hebt. Waarom gaat men daar dan uberhaupt tijd in steken om er software in/mee te maken? Wie ben je dan aan het belemmeren, toch alleen jezelf?
Imo kun je maar best even afkijken van databases wanneer je serializet. Veel concepten van daar zijn, zoals je aangeeft met SOAP, redelijk weerspiegelbaar binnen XML en andere mogelijkheden.
Ik weet niet of je het weet maar het probleem van deze thread heb ik al een aantal jaren geleden opgelost. Ja, het was even wat werk, maar dan werkt het ook gewoon. En het heeft geen reet met databases te maken, maar alles met de perceptie dat de XML die men verkrijgt na serializatie de OBJECT graph voorstelt, maar dat is niet zo. Vandaar ook dat die xmlserializer geen circular references aankan, want dat is nl. niet nodig: xml kan dat nl. ook niet.
Hier begin je weer over frameworks. (Waarom?)
Niet iedere taal heeft frameworks. Soms moet je echt alles lekker zelf schrijven en dan kan ik het je op een blaadje geven: vermijd toch maar circular references waar mogelijk.
Wellicht moet je even gaan uitleggen waarom die circular references toch vermeden moeten worden, ipv dat je het blijft roepen zonder enige context/uitleg. En dat JIJ jezelf wilt belemmeren door circular references niet te gebruiken... tja, dat is jouw probleem. JIJ realiseert je echter nu NIET dat deze thread door veel mensen gelezen wordt en dat die nu dus gaan twijfelen of ze circular references moeten vermijden of niet. Zo krijg je de mythes de wereld in, en daar zijn er al veel te veel van in de software engineering wereld.
Dat jij met "superiere" frameworks werkt (proficiat daarvoor overigens) die virtueel alles voor jou doen (waar hou jij je dan dagelijks nog mee bezig eigenlijk aangezien je overbodig bent dan) is jouw zaak.
Ik werk met .NET, geen idee of het superieur is, het werkt. Verder bouw ik al jaren frameworks zodat anderen zich niet druk hoeven te maken over triviale zaken als circular references. En dat gaat prima.
Denk ook even in andermans leefwereld waar niet alles vanzelfsprekend is. Dat jouw frameworks er nou net mee overweg kunnen is goed voor jou. Ik wed dat al jouw objecten parentreferences hebben zelfs al heb je ze niet nodig, OMDAT het kan.
Nee, als ze niet nodig zijn dan zijn ze er niet, maar veelal wil je de references wel omdat deze makkelijk zijn. Echter, wat JIJ roept is dat ze dan in DIE situaties nog steeds niet moeten worden toegevoegd, want dan stort de wereld in. Je komt dan zelfs met oplossingen aan die nog veel meer overhead veroorzaken terwijl een simpele reference de handel oplost.
Ik wil jou dan wel eens je frameworks afnemen en zien hoe je je zal ergeren op het slechte concept van backpointers.
Als ik terug moet navigeren in een graph dan heb ik echt die backreference nodig.

Sterker, entity management in-memory is gebaseerd op het bestaan van backreferences en laten die nou net erg handig zijn. Bij jou zijn die references er wel degelijk maar moet jij naar een aparte storage om die reference te realiseren. Dat lijkt het probleem wat jij kennelijk hebt met back references alleen maar te verplaatsen
Toen ik gisteren even de LoD opfriste om te zien dat die er eigenlijk niets mee te maken had kwam ik dit tegen:
http://www.builderau.com....028292,339180181-2,00.htm

En nu ik er nog even op google kom ik dit tegen:
http://smallwiki.unibe.ch/context/examples/

En op dit laatste staat eigenlijk dat LoD er wel mee te maken heeft.
Als jij backpointers implementeert dan ga je automatisch via die backpointer naar objecten waar je eigenlijk niet mee mag "praten".
Van WIE niet? Leg nu eens uit waarom niet, ipv als een kip zonder kop te blijven doorhameren dat iets 'slecht' is zonder aan te geven waarom.
(Nu herinner ik me weer waarom ik die 2 samen in 1 presentatie-sessie gekregen heb. Ik had er enkel de conclusies even van onthouden.)
Dus eigenlijk staat mijn eerste comment in deze thread nog hard overeind.

Doe je dit niet dan ga jij waarschijnlijk methodes als:
Order.GetCustomerAddress en ook Order.ItemPrice(...)
implementeren.
[...]
Waarom zou ik dat doen? Ik kan van order naar customer dus waarom moet ik dat soort bout code in de order class stoppen? JIJ kunt niet van order naar customer daarentegen, dus jij hebt dat probleem wel degelijk.
Om die graaf te serializen ga je toch meestal een vertrekpunt hebben. Een soort root vanwaar alles vertrekt. Volgens mij is de ideale oplossing om conceptueel breadth-first door je graaf te lopen en enkel forward links hard op te slaan en de back en side-links op te slaan als soft-references.
Als je bovendien je objecten ook (zoals ik hierboven al aangaf) enkel die soft-references laat opslaan met daarbij de juiste methodes die wel het juiste object teruggeven adhv die soft-reference dan is je probleem imo opgelost.
Ik heb helemaal geen soft references of hoe jij ze ook noemen wilt nodig en toch kan het in O(n), waar n het aantal nodes in de graph is. Sterker, ik kan overal beginnen en het serializet de gehele graph.

Het punt is nl, dat je je los moet trekken van het concept 'graph' in XML. de XML is de data in de objects. Jij krijgt nl. problemen wanneer je een serie orders wilt gaan serializen en meerdere orders hebben dezelfde customer. OOK met jouw soft references loop je tegen het punt aan dat de customer maar 1 keer geserialized kan worden. Je krijgt dan het leuke fenomeen dat je dit krijgt:

XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<orders>
    <order>
        <orderid>10</orderid>
        ...
        <customer>
            <customerid>12</customerid>
            ..
        </customer
    </order>
    <order>
        <orderid>11</orderid>
        ...
        <customer>
            <customerid>12</customerid>
            ..
        </customer
    </order>
    ...
</orders>

Customer lijkt hier de child te zijn van order. Maar jij wilt hem zien als parent. Verder staat customer 12 hier 2 keer in, dat kan natuurlijk niet.

Om deze problemen te vermijden ga jij roepen: "circular references zijn slecht!", maar dat maakt alleen je environment maar beperkt, het lost het nl. niet op. Als je het zo had opgelost:
XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<orders>
    <order>
        <orderid>10</orderid>
        ...
        <customer refid="12">
            <customerid>12</customerid>
            ..
        </customer
    </order>
    <order>
        <orderid>11</orderid>
        ...
        <customer refid="12"/>
    </order>
    ...
</orders>

(noem maar iets), dan had je het opgelost EN je had gewoon circular references kunnen gebruiken. Immers, dit kun je gewoon weer van boven naar beneden aflopen, wanneer je een refid attribute tegenkomt sla je de id en instance op in een lijst. Wanneer je een reference ziet zoals die 2e customer reference, dan sla je de ontvanger, property descriptor en id op in een lijst.

Aan het eind fiets je die laatste lijst nog eens af en insert de objects mbv de 1e lijst en de property descriptors.

Het is niet zo moeilijk, het is alleen even verder denken dan je neus lang is.

Zodra je gaat accepteren dat iets niet kan en dat dan als reden zien om iets NIET te doen, ben je in software engineering land IMHO verkeerd bezig. Je moet eerst goed beargumenteren waarom iets niet goed is. Zomaar iets roepen dat het DUS slecht is, is IMHO de verkeerde weg.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

EfBe schreef op zondag 18 maart 2007 @ 11:25:
[...]

Een ID is semantisch, een pointer is een harde referentie. Dus ik had het over een pointer. Object A referenced object B.


[...]

Wat houdt 'goed' in in deze context? Let wel: als 'goed' hier inhoudt dat het dan werkt omdat de infrastructure code dan niet uit elkaar valt dan ben je niet goed bezig.


[...]

Een order heeft geen customer? Sorry maar ik moet daar toch echt hard om lachen. Verder is het een voorbeeld. Jij beknot jezelf met references want 'oh jee, circular references zijn slecht!!!111', terwijl je niet kunt aangeven waarom.


[...]

En wat als ik een in-memory object graph heb? Jij wilt die t.a.t. een DAG laten zijn, maar dat is natuurlijk onzin.


[...]

En hoe krijg jij die parent dan tevoorschijn? Je kunt immers niet naar die parent navigeren in jouw briljante code.

Sorry, maar ik vind wat je voorstelt maar onzinnig. Het levert immers geen ENKEL voordeel op, alleen nadelen: extra code, extra roundtrips naar persistent storage wellicht, complexere algoritmen...


[...]

Dan is die GC een belemmering in de vrijheid die je hebt om software te maken, want je kunt dan dus iets NIET in die taal maken, bv een double-linked list. M.a.w.: een omgeving waar je niet veel aan hebt. Waarom gaat men daar dan uberhaupt tijd in steken om er software in/mee te maken? Wie ben je dan aan het belemmeren, toch alleen jezelf?


[...]

Ik weet niet of je het weet maar het probleem van deze thread heb ik al een aantal jaren geleden opgelost. Ja, het was even wat werk, maar dan werkt het ook gewoon. En het heeft geen reet met databases te maken, maar alles met de perceptie dat de XML die men verkrijgt na serializatie de OBJECT graph voorstelt, maar dat is niet zo. Vandaar ook dat die xmlserializer geen circular references aankan, want dat is nl. niet nodig: xml kan dat nl. ook niet.


[...]

Wellicht moet je even gaan uitleggen waarom die circular references toch vermeden moeten worden, ipv dat je het blijft roepen zonder enige context/uitleg. En dat JIJ jezelf wilt belemmeren door circular references niet te gebruiken... tja, dat is jouw probleem. JIJ realiseert je echter nu NIET dat deze thread door veel mensen gelezen wordt en dat die nu dus gaan twijfelen of ze circular references moeten vermijden of niet. Zo krijg je de mythes de wereld in, en daar zijn er al veel te veel van in de software engineering wereld.


[...]

Ik werk met .NET, geen idee of het superieur is, het werkt. Verder bouw ik al jaren frameworks zodat anderen zich niet druk hoeven te maken over triviale zaken als circular references. En dat gaat prima.


[...]

Nee, als ze niet nodig zijn dan zijn ze er niet, maar veelal wil je de references wel omdat deze makkelijk zijn. Echter, wat JIJ roept is dat ze dan in DIE situaties nog steeds niet moeten worden toegevoegd, want dan stort de wereld in. Je komt dan zelfs met oplossingen aan die nog veel meer overhead veroorzaken terwijl een simpele reference de handel oplost.


[...]

Als ik terug moet navigeren in een graph dan heb ik echt die backreference nodig.

Sterker, entity management in-memory is gebaseerd op het bestaan van backreferences en laten die nou net erg handig zijn. Bij jou zijn die references er wel degelijk maar moet jij naar een aparte storage om die reference te realiseren. Dat lijkt het probleem wat jij kennelijk hebt met back references alleen maar te verplaatsen


[...]

Van WIE niet? Leg nu eens uit waarom niet, ipv als een kip zonder kop te blijven doorhameren dat iets 'slecht' is zonder aan te geven waarom.


[...]

Waarom zou ik dat doen? Ik kan van order naar customer dus waarom moet ik dat soort bout code in de order class stoppen? JIJ kunt niet van order naar customer daarentegen, dus jij hebt dat probleem wel degelijk.


[...]

Ik heb helemaal geen soft references of hoe jij ze ook noemen wilt nodig en toch kan het in O(n), waar n het aantal nodes in de graph is. Sterker, ik kan overal beginnen en het serializet de gehele graph.

Het punt is nl, dat je je los moet trekken van het concept 'graph' in XML. de XML is de data in de objects. Jij krijgt nl. problemen wanneer je een serie orders wilt gaan serializen en meerdere orders hebben dezelfde customer. OOK met jouw soft references loop je tegen het punt aan dat de customer maar 1 keer geserialized kan worden. Je krijgt dan het leuke fenomeen dat je dit krijgt:

XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<orders>
    <order>
        <orderid>10</orderid>
        ...
        <customer>
            <customerid>12</customerid>
            ..
        </customer
    </order>
    <order>
        <orderid>11</orderid>
        ...
        <customer>
            <customerid>12</customerid>
            ..
        </customer
    </order>
    ...
</orders>

Customer lijkt hier de child te zijn van order. Maar jij wilt hem zien als parent. Verder staat customer 12 hier 2 keer in, dat kan natuurlijk niet.

Om deze problemen te vermijden ga jij roepen: "circular references zijn slecht!", maar dat maakt alleen je environment maar beperkt, het lost het nl. niet op. Als je het zo had opgelost:
XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<orders>
    <order>
        <orderid>10</orderid>
        ...
        <customer refid="12">
            <customerid>12</customerid>
            ..
        </customer
    </order>
    <order>
        <orderid>11</orderid>
        ...
        <customer refid="12"/>
    </order>
    ...
</orders>

(noem maar iets), dan had je het opgelost EN je had gewoon circular references kunnen gebruiken. Immers, dit kun je gewoon weer van boven naar beneden aflopen, wanneer je een refid attribute tegenkomt sla je de id en instance op in een lijst. Wanneer je een reference ziet zoals die 2e customer reference, dan sla je de ontvanger, property descriptor en id op in een lijst.

Aan het eind fiets je die laatste lijst nog eens af en insert de objects mbv de 1e lijst en de property descriptors.

Het is niet zo moeilijk, het is alleen even verder denken dan je neus lang is.

Zodra je gaat accepteren dat iets niet kan en dat dan als reden zien om iets NIET te doen, ben je in software engineering land IMHO verkeerd bezig. Je moet eerst goed beargumenteren waarom iets niet goed is. Zomaar iets roepen dat het DUS slecht is, is IMHO de verkeerde weg.
om deze zinloze discussie te beeindigen:
wat baat het een goeie schrijver te zijn als je neit eens kan lezen.

mocht je nou even stoppen met alles wat ik zeg uit z'n context te rukken, dan konden we mss een discussie voeren.

edit:
omdat ik het toch niet kan laten, de enige echte reden:
onderhoudbaarheid.

edit2:
en dan een voorbeeldje erbij:

vandaag:
- order heeft een reference naar customer.
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Customer
{
  GetAddress();
}

class Order
{
  Customer& GetCustomer();
  ProductList GetProducts();
}

// en dan heb je hopen code zoals:
void ShipOrder(Order& o)
{
  ShippingOrder ProduceShippingOrder(o.GetCustomer().GetAddress(), o.GetProducts());
  ShippindOrderList.Send(shippingOrder);
  Invoice ProduceInvoid(o.GetCustomer(), o.GetProducts());
  InvoiceList.Send(Invoice);
}


En dan komt op de IT afdeling de vraag binnen om de software zo aan te passen:
- er moeten orderlists komen. Een lijst van orders die in 1 maal besteld worden.
- Er bestaan plots 2 soorten customers: De eigenlijke besteller (bvb een bouwfirma die goederen koopt - bestelt orderlists) en de eigenlijke klant van de firma waar ook de goederen moeten geleverd worden (heeft een order gekoppeld). Soms zijn die gelijk, soms ook niet. Natuurlijk is degene die betaald dan ook verschillend van situatie tot situatie.

Daar gaat je supercode met al je frameworks. Daar gaat maanden werk verloren aan idioot refactoren.

Dit is een idioot probleem en je gaat het waarschijnlijk weer uit z'n context sleuren en er nog een slechter tegenvoorbeeld bij geven of een al even idiote niet-onderhoudbaar oplossing.
Als parent-references binnen je dagelijks gebruikte tools horen, dan ben je imo slecht bezig.

Ik kom ze dagelijks tegen en telkens we een nieuwe feature moeten ontwikkelen lopen we weerom tegen problemen aan omdat men vroeger wel parent-references bij overvloed gebruikte. (En ja er zijn verzachtende omstandigheden als tijdsdruk en andere).

einde discussie van mijn kant.

[ Voor 11% gewijzigd door H!GHGuY op 18-03-2007 12:49 ]

ASSUME makes an ASS out of U and ME


  • EfBe
  • Registratie: Januari 2000
  • Niet online
Dus een double linked list is in jouw ogen een debiele construct, om maar iets te noemen met back references?

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

EfBe schreef op zondag 18 maart 2007 @ 13:25:
Dus een double linked list is in jouw ogen een debiele construct, om maar iets te noemen met back references?
kom even van je paard af en open je ogen. Je trekt weer alles uit de context.

ASSUME makes an ASS out of U and ME


  • EfBe
  • Registratie: Januari 2000
  • Niet online
H!GHGuY schreef op zondag 18 maart 2007 @ 13:41:
[...]
kom even van je paard af en open je ogen. Je trekt weer alles uit de context.
Err..., jij roept zo hard je kunt dat back references slecht zijn. Ik zeg dan dat ik het daar niet mee eens ben, en dan zit ik op een paard en heb mijn ogen dicht.

Tja...

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

wat mij betreft ging de discussie over normale objectenstructuren in normale alledaags programma's.
niet over interne representaties van containers en andere.

Ik zeg ook niet dat backreferences _altijd_ slecht zijn. Ik zeg dat ze, mits uitzonderingen, slecht zijn.
Bovendien is dat imo geen _pure_ backreference. Het maakt bovendien deel uit van je requirements, namelijk in beide richtingen kunnen navigeren over de elementen in je container.

Je zegt dan wel dat ik geen hard argument heb, wat ik je dan meteen incl een voorbeeldje door je strot heb geramd. Vervolgens kom je met iets compleet anders, zonder ook maar over de essentie van de zaak iets te reppen.
Ik heb bovendien ook nog geen enkel hard argument gehoord waarom backreferences _GOED_ zijn. Ik durf bovendien wedden dat ik je in elk _goed_ voorbeeld waarin backreferences _niet slecht_ zijn zal kunnen bijstaan in je gelijk. Maar het punt blijft: in het overgrote merendeel van de gevallen duiden backreferences op een ronduit slecht design.

Als dat je manier is om je gelijk te halen... Wat een fijne collega moet jij zijn.

Stop dus met mijn woorden uit hun context te rukken, te verdraaien en een irritante manier hoog te houden van discussieren. Je brengt op die manier niets bij aan het hele topic en aan dit hele forum.

ASSUME makes an ASS out of U and ME


  • whoami
  • Registratie: December 2000
  • Laatst online: 00:54
Ik moet hier toch EfBe bijtreden; circular references zijn niet slecht, en ik zie ook niet echt in waarom het de onderhoudbaarheid bemoeilijkt.
Wat circulair references wel als 'nadeel' hebben, is dat ze het leven van de programmeur kunnen bemoeilijken in bepaalde situaties, maar dat wil niet zeggen dat ze slecht zijn.

Iig, met deze discussie is de topicstarter nog steeds niet geholpen; als hij die circular ref's nodig heeft, dan is dat zo. Ik heb gisteravond even snel een simpel probeerseltje gemaakt mbvh het implementeren van IXmlSerializable, en daarmee zal het wel op te lossen zijn, maar ik vraag me af of er geen makkelijkere te implementeren oplossing is. Het zou bv makkelijk oplosbaar moeten zijn moest die XmlSerializer nu eens rekening houden met het [Deserialized] attribute, of met de IDeserializeCallback interface, maar dat doet ie dus neit.
wat mij betreft ging de discussie over normale objectenstructuren in normale alledaags programma's.
niet over interne representaties van containers en andere.
Een Klant met een OrderCollection, en een Order met een reference naar Klant is echt wel alledaags; echter, ik moet ook wel zeggen dat ik voor het gemak hier zou verkiezen om de order-collection niet bij Klant bij te houden. Als het nodig is om de Orders van een bepaalde klant te hebben, dan kan je ze uit de DB trekken. Echter, er zijn vast wel real-world situaties waar die 2 way reference best nodig is.
in het overgrote merendeel van de gevallen duiden backreferences op een ronduit slecht design.
Waarom ? Hoe kan je dat onderbouwen ?

https://fgheysels.github.io/


  • EfBe
  • Registratie: Januari 2000
  • Niet online
H!GHGuY schreef op zondag 18 maart 2007 @ 18:43:
wat mij betreft ging de discussie over normale objectenstructuren in normale alledaags programma's.
niet over interne representaties van containers en andere.

Ik zeg ook niet dat backreferences _altijd_ slecht zijn. Ik zeg dat ze, mits uitzonderingen, slecht zijn.
Says who? Kijk, jij mag beweren dat ze slecht zijn, echter de lezers van deze thread krijgen alleen de boodschap DAT ze kennelijk slecht zijn, zonder onderbouwing. Daar ageer ik tegen, temeer omdat jij beweert dat ze een teken zijn van slecht design volgens jou. Nou wil ik niet arrogant klinken maar WTF ben jij om dat soort designs slecht te noemen?
Bovendien is dat imo geen _pure_ backreference. Het maakt bovendien deel uit van je requirements, namelijk in beide richtingen kunnen navigeren over de elementen in je container.

Je zegt dan wel dat ik geen hard argument heb, wat ik je dan meteen incl een voorbeeldje door je strot heb geramd. Vervolgens kom je met iets compleet anders, zonder ook maar over de essentie van de zaak iets te reppen.

Ik heb bovendien ook nog geen enkel hard argument gehoord waarom backreferences _GOED_ zijn. Ik durf bovendien wedden dat ik je in elk _goed_ voorbeeld waarin backreferences _niet slecht_ zijn zal kunnen bijstaan in je gelijk. Maar het punt blijft: in het overgrote merendeel van de gevallen duiden backreferences op een ronduit slecht design.
Opnieuw: Says who? Jij? En jij bent... de design guru van nederland?

Verder hoef ik niet te bewijzen dat jij ongelijk hebt. Als iemand beweert dat roze olifantjes bestaan dan is het niet mijn taak aan te tonen dat ze niet bestaan. Jij beweert dat backreferences duiden op een slecht design. Nou, ik zou zeggen, ga dat dan maar eens aantonen.
Als dat je manier is om je gelijk te halen... Wat een fijne collega moet jij zijn.
:?

Ik ben het niet met je eens en nu is het persoonlijk of zo?
Stop dus met mijn woorden uit hun context te rukken, te verdraaien en een irritante manier hoog te houden van discussieren. Je brengt op die manier niets bij aan het hele topic en aan dit hele forum.
1, 2, 3, 4, ..... 10. :/

Als je goed had opgelet dan heb ik de oplossing voor de topic starter al aangedragen indeze post: EfBe in "\[.NET] XmlSerializer en circulaire verwi...", aan het eind. Als de topicstarter dat dan even goed leest, en een paar uur werk ertegenaan gooit, dan lukt het wellicht om dit gewoon op te lossen zodat de topicstarter gewoon een objectgraph MET cyclic references naar XML kan serializen EN deserializen.

Niets bijgedragen aan dit topic? Ik weet wel iemand die iig niet iets zinnigs aan dit topic heeft bijgedragen of heb jij de oplossing voor de topicstarter ergens gepost?

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • MicroWhale
  • Registratie: Februari 2000
  • Laatst online: 01-12 10:46

MicroWhale

The problem is choice

Waarom zou je dit willen doen?

Ik heb geleerd om informatie (in welke vorm dan ook) altijd zo atomair mogelijk op te slaan. Dat betekent dat alleen datgene wat uniek en essentieel is voor dat record, object, etc... opgeslagen wordt. Alle verwijzingen/referenties worden dan als zodanig opgeslagen.

Bijvoorbeeld: Als een order refereert naar een product of een klant dan zal in de order enkel een referentie naar een bepaald (uniek identificeerbaar) product of bepaalde klant bevatten, maar niet de informatie over het product of de klant zelf. Laatstgenoemde informatie is in een andere informatiekern opgeslagen en hoort imo niet in de orderinformatie thuis.

Het enige belangrijke is dat je vandaag altijd rijker bent dan gisteren. Als dat niet in centen is, dan wel in ervaring.


  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

EfBe schreef op zondag 18 maart 2007 @ 23:45:
Says who? Kijk, jij mag beweren dat ze slecht zijn, echter de lezers van deze thread krijgen alleen de boodschap DAT ze kennelijk slecht zijn, zonder onderbouwing. Daar ageer ik tegen, temeer omdat jij beweert dat ze een teken zijn van slecht design volgens jou. Nou wil ik niet arrogant klinken maar WTF ben jij om dat soort designs slecht te noemen?

Opnieuw: Says who? Jij? En jij bent... de design guru van nederland?
gelukkig niet, want ik ben geen nederlander.

Ik heb je reeds een minimalistisch voorbeeld gegeven, wat je in deze hele monoloog (een discussie is het niet aangezien je niets leest wat ik schrijf) duidelijk aantoont dat de onderhoudbaarheid van je software er serieus kan onder leiden.
Verder hoef ik niet te bewijzen dat jij ongelijk hebt. Als iemand beweert dat roze olifantjes bestaan dan is het niet mijn taak aan te tonen dat ze niet bestaan. Jij beweert dat backreferences duiden op een slecht design. Nou, ik zou zeggen, ga dat dan maar eens aantonen.
Toch wel.
Ik ken geen enkele discussie waarbij slechts 1 partij moet argumenten aanvoeren.
Als je goed had opgelet dan heb ik de oplossing voor de topic starter al aangedragen indeze post: EfBe in "\[.NET] XmlSerializer en circulaire verwi...", aan het eind. Als de topicstarter dat dan even goed leest, en een paar uur werk ertegenaan gooit, dan lukt het wellicht om dit gewoon op te lossen zodat de topicstarter gewoon een objectgraph MET cyclic references naar XML kan serializen EN deserializen.

Niets bijgedragen aan dit topic? Ik weet wel iemand die iig niet iets zinnigs aan dit topic heeft bijgedragen of heb jij de oplossing voor de topicstarter ergens gepost?
Opnieuw: leer lezen.

Ik heb meer nog dan jou een structureel mooie oplossing gegeven. Als hij de boel vervangt door een soft-reference dan is zijn probleem ook opgelost.
Bovendien moet jij, die zo hard op frameworks kickt, niet komen aandraven over extra round-trips naar persistent storage. Een beetje deftig framework cachet objecten.

En omdat je dit ook zou kunnen negeren:
1) Wie de LoD toepast gebruikt zowiezo geen backpointers.
van de LoD is bewezen dat ze de kwaliteit van software behoorlijk kan verhogen.
2) Er zijn gevallen waarin backpointers noodzakelijk zijn.
3) Ik werk dagelijks in software waarin backpointers een gehaat fenomeen zijn die we er liever vroeger dan later uithalen. Code met verwijzingen als de volgende over verschillende modules heen
C++:
1
2
3
4
5
6
7
8
9
void SomeClass::SomeFunction()
{
  if (GetParent()->GetParent()->SomeFunction())
  {
    GetParent()->SetSomeProperty();
    GetParent()->GetParent()->RecheckChildren();
    UpdateSelf();
  }
}

zijn geen ongekend fenomeen.

Als je dan weet dat er enkele extra niveaus in de hierarchie moeten komen dan weet je welke heibel ervan komt. Denk dus niet dat de impact op onderhoudbaarheid pure nonsense is.

Een goed voorbeeld van parent pointers met enige rechtvaardiging is het System.Windows.Forms in .NET. Op zich kent Button zijn container niet, behalve dat het een container is. De buttonclick events worden dan weer niet afgehandeld in Button (geen code ala (Form)GetParent().SetText("blah")) maar in de afgeleide van Form (dus this.SetText("blah") in de buttonclick eventhandler)

Wanneer Button specifiek code zou hebben voor een Form als parent of voor een Grid als parent dan heb je een serieuze design flaw want wat doe je dan als je je button wil toevoegen op een ander component? Dat Button weet dat hij in een ControlContainer aanwezig is, kan gerechtvaardigd worden.

Om van het ene voorbeeldje op het andere over te springen:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Car
{
public:
  AddWheel(Wheel w);
  Wheel[] GetWheels();
}

class Wheel
{
  SetParent(Car c);
  Brand GetBrand();
  double GetDiameter();
  double GetWidth();
  double GetPressure();
}

Wat doe jij dan om een wiel op je fiets te steken, of een autoband op je fiets?
Car-specifieke code hoort in Car thuis en Wheel-specifieke code in Wheel.
Je mag nu in je haar krabben en zeggen dat ik iets evident vertel, maar code zoals hierboven leidt al gauw tot dit:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Engine
{
  double GetRequiredHP();
  void SetRequiredHP();
}
class Car
{
  Engine GetEngine();
}
class Wheel
{
  void SetPunctured()
  {
    GetParent().GetEngine().SetRequiredHP(GetParent().GetEngine().GetRequiredHP() + 1);
  }
}

wil je nog steeds een autoband op je fiets (in dit geval meteen tot bromfiets herleid) of een fietswiel op je auto?

ASSUME makes an ASS out of U and ME


  • whoami
  • Registratie: December 2000
  • Laatst online: 00:54
H!GHGuY schreef op maandag 19 maart 2007 @ 19:58:
[...]
En omdat je dit ook zou kunnen negeren:
1) Wie de LoD toepast gebruikt zowiezo geen backpointers.
van de LoD is bewezen dat ze de kwaliteit van software behoorlijk kan verhogen.
Ik blijf me afvragen waarom je steeds de LoD er bij betrekt, want deze heeft hier imho niets mee te maken.
Om van het ene voorbeeldje op het andere over te springen:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Car
{
public:
  AddWheel(Wheel w);
  Wheel[] GetWheels();
}

class Wheel
{
  SetParent(Car c);
  Brand GetBrand();
  double GetDiameter();
  double GetWidth();
  double GetPressure();
}

Wat doe jij dan om een wiel op je fiets te steken, of een autoband op je fiets?
Car-specifieke code hoort in Car thuis en Wheel-specifieke code in Wheel.
Je mag nu in je haar krabben en zeggen dat ik iets evident vertel, maar code zoals hierboven leidt al gauw tot dit:
Tja, hier beperk je enkel jezelf. Een circular reference is hier ook perfect mogelijk, en is ook uitbreidbaar, moest je er niet van uitgaan dat e parent altijd een Car is, maar een IVehicle of een Vehicle (abstract class) bv. Dat is trouwens hetgeen er ook in WinForms gedaan wordt; De Parent van een Button is geen Form maar een Control.

(Je naamgeving is trouwens niet goed, want je Car is geen parent van je Wheel; je Car is de owner van je Wheel).

En laten we even ophouden met op de man te spelen.

[ Voor 4% gewijzigd door whoami op 19-03-2007 20:08 ]

https://fgheysels.github.io/


  • MrBucket
  • Registratie: Juli 2003
  • Laatst online: 29-10-2022
Hmm... de verbale matpartij is nog steeds aan de gang zie ik ;)

Alhoewel ik de indruk krijg dat deze discussie zich nu meer op het algemene niveau van "moet je wel of geen backreferences gebruiken" bevindt, is het misschien toch aardig om wat kenmerken van mijn situatie te schetsen om enerzijds de discussie wat meer te focussen en anderzijds te verklaren waarom ik in de OP aangaf dat circulaire verwijzingen conceptueel niet verkeerd zijn.
  1. De business-objecten bevatten bijna geen logica, en kennen geen operaties die zij op elkaar uitvoeren. Het enige nut van een referentie ligt gelegen in het feit dat deze twee objecten "wat met elkaar te maken hebben". Bij nader inzien is de term 'business object' misschien iets te ruim gekozen, en zou je misschien moeten spreken van data containers ofzo. Jullie (hooggewaardeerde input, overigens :*) ) met betrekking tot meer geavanceerde designpatterns speelt voor mij in dit geval weinig tot niet.
  2. De graaf van objecten wordt aan de client-kant niet strikt vanuit 1 hoofd-entiteit bekeken, maar vanuit meerdere invalshoeken. Zo heb je een view vanuit het perspectief van Bedrijven, waaronder Projecten hangen, en vervolgens Medewerkers. Daarnaast heb je echter ook een view vanuit een Medewerker, waarbij je kunt zien welke Projecten men heeft gedaan bij welke Bedrijven, etc. Het ontbreken van backreferences maakt het dus heel lastig om de informatie die nodig is vanuit alle perspectieven te destilleren.
@MrWilliams:
Het lijkt erop alsof je het hebt over de principes zoals die gelden voor een goed database-design (normalisatie)? Deze principes gelden niet per se 1 op 1 voor een goed OO-design, imo.

  • MicroWhale
  • Registratie: Februari 2000
  • Laatst online: 01-12 10:46

MicroWhale

The problem is choice

Ongeacht de vorm:
Bij het opslaan van informatie geldt (imo) altijd dat je redundantie moet beperken tot 0, tenzij het écht niet anders kan. Zo voorkom je inconsistentie van je informatie bij mutatie.

Mocht je complexe OO-structuren op willen slaan, probeer dit dan eens te modelleren in een OO-database. Het model wat hieruit voortkomt, kun je wellicht gebruiken als Xml-Serialisatiemodel.

Het enige belangrijke is dat je vandaag altijd rijker bent dan gisteren. Als dat niet in centen is, dan wel in ervaring.


  • whoami
  • Registratie: December 2000
  • Laatst online: 00:54
MrWilliams schreef op dinsdag 20 maart 2007 @ 09:35:
Ongeacht de vorm:
Bij het opslaan van informatie geldt (imo) altijd dat je redundantie moet beperken tot 0, tenzij het écht niet anders kan. Zo voorkom je inconsistentie van je informatie bij mutatie.
Als het gaat om relationele modellen ben ik het er mee eens, in een OO omgeving moet je dit nuanceren:
In het geval van een circulaire referentie; bv. Een klant heeft een OrderCollection, en een Order verwijst naar de Klant, dan heb je ook geen redundantie. Het Order zal nl. naar hetzelfde klant-object wijzen als het Klant object dat die Order bevat.
Ik zie niet in wat je hier dan hebt qua redundantie.
Mocht je complexe OO-structuren op willen slaan, probeer dit dan eens te modelleren in een OO-database. Het model wat hieruit voortkomt, kun je wellicht gebruiken als Xml-Serialisatiemodel.
:?
Waarom zou je uw manier van opslaan van gegevens gaan 'opofferen' ? De manier waarop MrBucket de gegevens opslaat is gewoon goed. Ik zie niet in waarom hij een OODBMS zou moeten gebruiken om te kijken hoe hij het beste die gegevens opslaat ?

https://fgheysels.github.io/


  • EfBe
  • Registratie: Januari 2000
  • Niet online
(Ik heb maar wat om de flames heen gewerkt, dat viel nog niet mee)
H!GHGuY schreef op maandag 19 maart 2007 @ 19:58:
Ik heb je reeds een minimalistisch voorbeeld gegeven, wat je in deze hele monoloog (een discussie is het niet aangezien je niets leest wat ik schrijf) duidelijk aantoont dat de onderhoudbaarheid van je software er serieus kan onder leiden.
Nou, ik ben nog opgegroeid in unix/C en ik ben niet onder de indruk van je bezwaren: een backreference moet je inderdaad onderhouden, maar dat moet je met alle references. Die backreferences heb je nodig om vrij door een graph te kunnen navigeren.

Het rare is nl. dat jij dat wel erkent en dan met een overhead oplossing aankomt: een lijst met objects buiten je graph. Maar die moet je OOK onderhouden, sterker, die zijn lastiger te onderhouden.

Verder vind ik het ironisch dat je, wanneer je toch tegen de reference in wilt navigeren de objects maar opnieuw gaat ophalen. No offence, maar dat is wel erg moeilijk doen terwijl het makkelijk kan. En waarom? WAAROM zijn backreferences dan zo verschrikkelijk naar voor je dat je al die moeite doet?

Ik zie niet in wat er nu zo moeilijk aan is. Tja, als men niet snapt hoe je een double linked list moet updaten, dan wordt het inderdaad lastig, maar wellicht is het dan beter om maar te stoppen met software bouwen.
Toch wel.
Ik ken geen enkele discussie waarbij slechts 1 partij moet argumenten aanvoeren.
Oh, dus als jij wat beweert wat kant noch wal raakt dan is het aan de ander om aan te tonen dat je uit je nek lult? Rare manier van 'discussieren'.
Ik heb meer nog dan jou een structureel mooie oplossing gegeven. Als hij de boel vervangt door een soft-reference dan is zijn probleem ook opgelost.
Tja, zo kan ik ook makkelijk praten, maar dat betekent nog niet dat het ook daadwerkelijk opgelost is. soft references of hoe je ze ook noemen wilt, zijn niet bruikbaar met dezelfde constructs als de normale reference, verder moet je ze OOK bijhouden, en per saldo zijn die dingen nog lastiger bij te houden dan een simpele back reference.
Bovendien moet jij, die zo hard op frameworks kickt, niet komen aandraven over extra round-trips naar persistent storage. Een beetje deftig framework cachet objecten.
Ah, nog een 'makkelijk praten' opmerking die eigenlijk geen reet voorstelt. Cachen van objects? Ooit een persistence framework gemaakt met cache? Nee? Ik wel. Ik kan je verzekeren, die cache werkt niet zoals men dat te pas en te onpas roept, het lullige is namelijk dat die mensen geen reet van de hele materie afweten.
http://weblogs.asp.net/fb...ch-data-faster_2E00_.aspx

Ook de reden waarom in mijn framework geen cache zit, het heeft geen zin. Er zit wel een cache in, maar alleen voor single entity PK based fetches. Het lullige is echter dat dat niet 'gratis' is: de hoeveelheid operaties nodig om cache misses op te vangen of uberhaupt in een cache te zoeken is hoger dan een simpele '.' operator actie.

En DAAR gaat het om. Jij doet zo verschrikkelijk veel moeite om die . operator te vermijden. Ik kan alleen maar concluderen dat je dan niet echt bijster slim bezig bent.

Maar goed, aangezien jij allang vindt dat ik het niet snap, ga vooral zo door zou ik zeggen, ik hoop alleen voor jouw klanten / werkgever dat ze er niet teveel onder lijden.
En omdat je dit ook zou kunnen negeren:
1) Wie de LoD toepast gebruikt zowiezo geen backpointers.
van de LoD is bewezen dat ze de kwaliteit van software behoorlijk kan verhogen.
LoD? Je snapt er werkelijk weinig van. LoD zegt juist:"The fundamental notion is that a given object should assume as little as possible about the structure or properties of anything else (including its subcomponents)." (http://en.wikipedia.org/wiki/Law_of_Demeter). Dit houdt dus in dat van infrastructure limitaties je geen weet hebt, en als een Order een reference naar Customer zou moeten hebben, dan heeft Order dat. Order gaat er echter NIET van uit dat hij informatie kan betrekken van 3 niveaus diep bijvoorbeeld, maar dat heeft met DIT probleem niets te maken want die beperking is ook van toepassing op jouw parent -> child reference! Immers, de ENKELE reference die jij zo graat ziet is OOK gelimiteerd door LoD: customer kan niet weten van Order zijn child references.

M.a.w.: LoD heeft hier echt niets mee te maken, temeer omdat Order's backreference naar Customer gewoon een reference is naar een directe buur.
2) Er zijn gevallen waarin backpointers noodzakelijk zijn.
Je meent het...
3) Ik werk dagelijks in software waarin backpointers een gehaat fenomeen zijn die we er liever vroeger dan later uithalen.
Komt dat omdat de code geschreven is door prutsers of komt dat omdat het concept fout is?
Code met verwijzingen als de volgende over verschillende modules heen
C++:
1
2
3
4
5
6
7
8
9
void SomeClass::SomeFunction()
{
  if (GetParent()->GetParent()->SomeFunction())
  {
    GetParent()->SetSomeProperty();
    GetParent()->GetParent()->RecheckChildren();
    UpdateSelf();
  }
}

zijn geen ongekend fenomeen.
Dit heeft dus geen reet met backpointers te maken, maar WEL met LoD. Immers:
GetParent().GetParent().GetParent() kun je vervangen door:
Child.Child.Child.SomeMethod();
en dan heb je hetzelfde probleem, maar dan met forward references. Zie je nu dat wat jij beweert niet echt werkt en dat het NIET ligt aan de backreferences maar aan een design flaw?

En dat daargelaten, leg mij eerst maar eens uit waarom een method in class C een method moet aanroepen in class A die daar 2 hops vandaan zit. Die code die je hierboven gepost hebt is fout, maar niet omdat er backreferences in zitten, maar omdat je ipv GetParent().GetParent().GetParent().SomeMethod() je had moeten zeggen:
GetParent().SomeMethod(), waarbij de parent dus die method verder uitvoert, wellicht door zn parent te vragen of wat dan ook. Maar NOG beter was het geweest om de logica BUITEN deze class te plaatsen, immers het is een cross-class method geinitieert IN een van de classes. Veelal wordt dit dan ge-outsourced naar een derde class die het management van de routine op zich neemt.
Als je dan weet dat er enkele extra niveaus in de hierarchie moeten komen dan weet je welke heibel ervan komt. Denk dus niet dat de impact op onderhoudbaarheid pure nonsense is.
Tja, maar dat is jouw eigen architecturele blunder, dat heeft niets met backpointers te maken. Als jij 3 niveaus diep naar children zoekt en daar dingen aanroept gaat het net zo fout, en dat zijn dan FORWARD references. Dus, ga je die dan ook weghalen?

De kluwen van references naar allerlei objects in een graph zie je vaak terugkomen in DDD based code, waarbij men kost wat kost wil vermijden dat men aenemic classes krijgt, ("want boooee!!! dat is een antipattern volgens Blah Blah Bliki Fowler!") en dan met een probleem zit waar code te plaatsen waar een aantal andere objects ook bij betrokken zijn. Tja, terwijl dat zo simpel op te lossen is met manager classes, die als een service fungeren en een graph 'consumeren'. Dit verlost je van het feit dat de logica IN een van de objects zit die in de graph zit en die dus over de gehele graph moet navigeren.
Een goed voorbeeld van parent pointers met enige rechtvaardiging is het System.Windows.Forms in .NET. Op zich kent Button zijn container niet, behalve dat het een container is. De buttonclick events worden dan weer niet afgehandeld in Button (geen code ala (Form)GetParent().SetText("blah")) maar in de afgeleide van Form (dus this.SetText("blah") in de buttonclick eventhandler)
Ook dit heeft er niets mee te maken, want de reden waarom de handler van het button click event niet in button zit is heel simpel: het observer pattern zegt dat de observer zich BUITEN de te observen class bevindt, in dit geval is de button de te observen class en het form de observer.
Wanneer Button specifiek code zou hebben voor een Form als parent of voor een Grid als parent dan heb je een serieuze design flaw want wat doe je dan als je je button wil toevoegen op een ander component? Dat Button weet dat hij in een ControlContainer aanwezig is, kan gerechtvaardigd worden.
Niet echt, je kunt dan t.a.t. terugvallen op het observer pattern. Het enige is dat een button in weinig andere contexts gebruikt gaat worden dan op een form (direct of indirect). Een beter voorbeeld had kunnen zijn een willekeurig BO, dat bv in een persistence layer context zit maar ook in een grid (wellicht via een controller). Het BO weet dat niet, roept gewoon dat er iets is gewijzigd en de rest zoekt het maar uit.
Om van het ene voorbeeldje op het andere over te springen:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Car
{
public:
  AddWheel(Wheel w);
  Wheel[] GetWheels();
}

class Wheel
{
  SetParent(Car c);
  Brand GetBrand();
  double GetDiameter();
  double GetWidth();
  double GetPressure();
}

Wat doe jij dan om een wiel op je fiets te steken, of een autoband op je fiets?

Car-specifieke code hoort in Car thuis en Wheel-specifieke code in Wheel.
Je mag nu in je haar krabben en zeggen dat ik iets evident vertel, maar code zoals hierboven leidt al gauw tot dit:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Engine
{
  double GetRequiredHP();
  void SetRequiredHP();
}
class Car
{
  Engine GetEngine();
}
class Wheel
{
  void SetPunctured()
  {
    GetParent().GetEngine().SetRequiredHP(GetParent().GetEngine().GetRequiredHP() + 1);
  }
}

wil je nog steeds een autoband op je fiets (in dit geval meteen tot bromfiets herleid) of een fietswiel op je auto?
Sinds wanneer zou ik naar engine willen navigeren vanuit wheel? Logica in Wheel heeft betrekking op wheel, niet op andere objects. Dat soort logica plaats ik altijd in een externe class, temeer omdat het niet duidelijk is in welke van de betrokken classes de logica dan zou moeten. Voorbeeld: inventory management: je hebt: order, order lines, products, inventory per product, bestellingen etc. Ga je dat allemaal in order stoppen? In product? In de inventory per product? Of in een aparte class? Die aparte class is nl. prima in staat om te doen wat jij wilt, stateless. Het leuke is ook dat je de logica op 1 plek hebt en het makkelijk onderhoudbaar is. Een ander bijkomend voordeel is dat je backreferences dan wel degelijk kunt gebruiken, want je kunt makkelijk van de ene naar de andere navigeren, en guess what: beheer van die dingen is nog kinderspel ook.

Oh wacht, ik begreep het niet. Ah dat was het. :/

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


  • MicroWhale
  • Registratie: Februari 2000
  • Laatst online: 01-12 10:46

MicroWhale

The problem is choice

whoami schreef op dinsdag 20 maart 2007 @ 09:40:
[...]
Als het gaat om relationele modellen ben ik het er mee eens, in een OO omgeving moet je dit nuanceren:
In het geval van een circulaire referentie; bv. Een klant heeft een OrderCollection, en een Order verwijst naar de Klant, dan heb je ook geen redundantie. Het Order zal nl. naar hetzelfde klant-object wijzen als het Klant object dat die Order bevat.
Ik zie niet in wat je hier dan hebt qua redundantie.
Ik denk dat wij over hetzelfde praten. Ik had uit de OP van TS opgemaakt dat hij geen verwijzingen op wilde slaan, maar de werkelijke objecten in een object. Dat kan ik verkeerd gelezen hebben.
[...]
:?
Waarom zou je uw manier van opslaan van gegevens gaan 'opofferen' ? De manier waarop MrBucket de gegevens opslaat is gewoon goed. Ik zie niet in waarom hij een OODBMS zou moeten gebruiken om te kijken hoe hij het beste die gegevens opslaat ?
Dat impliceerde ik niet. Ik zei dat hij eens een model kon maken dat in een OO-database past, waarna hij het model kan toepassen op zijn eigen XmlSerializer.

[ Voor 19% gewijzigd door MicroWhale op 20-03-2007 12:59 ]

Het enige belangrijke is dat je vandaag altijd rijker bent dan gisteren. Als dat niet in centen is, dan wel in ervaring.


  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

je argumenteert nog steeds op de voorbeeldjes zelf, en niet op wat ik ermee wil aantonen.

Je hebt in deze hele discussie nog maar bitter weinig bezig geweest met wat ik probeer duidelijk te maken. Het enige wat je hebt gedaan is de voorbeeldjes die ik aangeef om een punt te maken afgebroken.

Je las over de woorden: "... code... leidt al gauw tot dit:"
Het is geen rocket-science dat je van Wheel niet naar Engine moet. Maar veel programmeurs zouden de neiging hebben om wanneer hun band plat staat de extra weerstand die ze daardoor ondervinden richtstreeks naar Engine te gaan verhalen. Zeker als er wat tijdsdruk is en hun feature buiten moet. "we lossen het later wel op"

Dat LoD er imo wel mee te maken heeft is dat die backreferences er nu net snel toe leiden dat iemand zomaar even langs die backpointer navigeert om wat logica uit te voeren. En dat code (zelfs buiten Wheel) zoals ik aangaf een kleine stap is.

Bovendien gaf je al meteen de aanzet tot een correctere oplossing:
C++:
1
2
3
4
5
6
7
8
void Wheel::SetPunctured
{
  Car* car = CarManager::GetCar(m_CarID);
  if (car)
  {
     car->AddRequiredHP(1);
  }
}

Natuurlijk is het beter om hiervoor een observer te gebruiken (voor je dit voorbeeld als dusdanig weer uit de lucht schiet) of op z'n minst interfaces.
Het punt is dat je op dergelijke manier naar een hoger niveau navigeert en daar de logica een enkel eenvoudig commando aanstuurt (dus ook geen child.child.Set...())

Wat die software betreft waar ik dagelijks in werk: nee, ik ben geen schuldige. Ik was er op dat moment nog niet en zoals ik aangaf: er zijn omstandigheden (aka management).

Het punt blijft imo staan:
een object op een lager niveau in de hierarchie spreekt niet rechtstreeks objecten aan hoger in de hierarchie. Moet je toch omhoog in de hierarchie gebruik dan een abstractielaag ipv backreferences.
Abstractielaag is dan bvb een observer of een zo generiek mogelijke interface. Dat een wiel op iets gemonteerd kan worden: logisch. Dat een wiel met een gat erin moet kunnen aangeven aan zijn "parent": logisch. Dat wiel in een IWielContainer zit: sure - zelfs IVehicle vind ik al te specifiek. Dat Wiel IWielContainer kent: sure - ik zou IWielContainer zelfs als "uitbreiding" van de interface van Wiel aanzien. Dat wiel de exacte class van zijn parent weet: fout. Dat Wiel de members van zijn gekende parent gebruikt: fout.

Kortom: zomaar backreferences gebruiken: fout.

Waar je ook telkens over leest is het feit dat ik container klassen zoals double linked lists , graphs e.a. buiten beschouwing laat. Die dingen hebben een volledig ander doel.
Het gaat erom dat implementatie klassen zoals bvb CMuis hun parent als CComputer kennen en gebruiken.

Als er nog zo'n onzinnig bericht komt, zal ik niet eens de moeite meer nemen om te replyen.
Dan is het genoeg geweest.

ASSUME makes an ASS out of U and ME


Verwijderd

Even voorbijgaand aan het verbale matten (prima te genieten met een colaatje en een bakkie chips achter de PC ;) )
H!GHGuY schreef op dinsdag 20 maart 2007 @ 20:04:
Het punt blijft imo staan:
een object op een lager niveau in de hierarchie spreekt niet rechtstreeks objecten aan hoger in de hierarchie.
Hier maak je m.i. een denkfout. Hierarchie is "in the eye of the beholder". Voor de debiteurenadministratie is de klant hierarchisch hoger dan de verschillende orders, maar voor diegene die 300 dezelfde orders uit 't magazijn haalt voor 300 verschillende klanten, is die klant minder belangrijk dan die order.
Moet je toch omhoog in de hierarchie gebruik dan een abstractielaag ipv backreferences.
Abstractielaag is dan bvb een observer of een zo generiek mogelijke interface.
Je hebt hier geen omhoog/omlaag, maar meer een links/rechts relatie. Bij meer dan 1 'hop' zou ik ook eerder kiezen voor een observer of een manager, maar tot die tijd zijn back references het handigst en efficientst om links en rechts met elkaar te laten praten.

Alleen kan de standaard XmlSerializer van .NET daar niet zo goed mee overweg, en ik meen me te herinneren dat 't daarom ging in dit topic... ;)
Pagina: 1