Kodning: Lækkende abstraktioner og deres evne til at holde vand

Jeg har lige siddet og arbejdet med at udvide noget kode, som serialiserer Java-bønner som JSON ved at bruge Gson. Det er gennemgående ret enkelt at tilføje flere properties til de serialiserede objekter, men pludselig gav hele molevitten sig til at dørke med en StackOverflowError. Lidt efterforskning viste hurtigt, at det var serialiseringen af en java.util.Date, som gik i selvsving - "Wat? Det fungerer ellers glimrende at serialisere datoer?"

Ondets rod viste sig at være, at det objekt, som skulle serialiseres, slet ikke var en Date, men derimod en instans af eller anden proxy-klasse, som nedarver fra Date, og som et ORM-framework har ændret bønnens getter-metode til at returnere ved at modificere den oversatte klassefil. Og når serialiseringen af instanser af den samme klasse tidligere er gået godt skyldes det formentlig, at noget tilfældigvis har fået sat deres indre tilstand, så der ikke er løkker i den objektgraf, de indgår i.

Med andre ord: noget udgiver sig for at være en Date, men er det alligevel ikke. Eller i hvert fald ikke altid. Eller i alle sammenhænge.

Den slags lækkende abstraktioner har været kilden til et enormt antal tilfælde af fejl og andre dårligdomme, som jeg gennem tiden er stødt på; både ved arbejdet med egen kode og i reviews af andres kode. Og det er ikke unikt for mig og mit problemdomæne - det var lidt af en aha-oplevelse for mig, da jeg engang for hvad der forekommer mig at være æoner siden snakkede med min svoger, som også er softwareudvikler; bare indenfor et helt andet område, og kunne konstatere, at han havde samme oplevelse. For mit eget vedkommende har de ovennævnte ORM-frameworks altid været en kilde til problemer (se fx også N+1 select), og for hans vedkommende var det fx et problem, at CPU'en lader til at eksponere maskinens hukommelse som et uniformt array af bytes, hvilket den meget langt fra er.

En diskussion af hvad der er galt med ORM-frameworks vil i øvrigt nok kunne fylde adskillige blogindlæg.

Anyway. Det med lækkende abstraktioner er et interessant problem, både fordi det er så hyppigt forekommende og fordi det ofte har konsekvenser, som først opdages sent. Mit indledende eksempel er lidt upædagogisk, fordi det var ret let at finde ud af, at ens kode dørkede, men i andre tilfælde opdager man først problemerne, når applikationen er gået i drift og man får skruet op for volumen af data. Er en del af forklaringen på Polsag-problemerne at finde her? Der er også rigtig pikante eksempler at finde på, at sådan nogen problemer kommer snigende, når man opgraderer trediepartskomponenter; den herostratisk berømte ændring af implementeringen af String.substring() i Java 7 ligger lige for.

Mere overordnet er der den udfordring, at man ofte forsøger at bortabstrahere så meget som muligt. Alan Knight siger fx

One of the great leaps in OO is to be able to answer the question “How does this work?” with “I don’t care”.

Intentionen er fin, men jeg oplever, at det ikke helt holder i praksis. Man bliver (desværre) en gang imellem nødt til at vide, hvordan ting fungerer, men det rigtig gode spørgsmål er så hvornår.

Hvordan sikrer man sig som individuel udvikler og som organisation, at man hverken ved for meget eller for lidt de abstraktioner man bruger? En vigtig forudsætning er selvfølgelig at være klar over, at abstraktioner kan lække (der er jo bl.a. en grund til, at der er mere end en implementering af de forskellige Collections-interfaces i JDK'et), men er der andre retningslinjer, som er gode at efterleve?

Mikkel Lauritsens billede
Mikkel er CTO i IntraMed, som laver applikationer til håndtering af behandlingsforløb for patienter med kroniske sygdomme.

Kommentarer (27)

Torben Mogensen Blogger

One of the great leaps in OO is to be able to answer the question “How does this work?” with “I don’t care”.

Det er min opfattelse, at dette er tilfældet for 99% af alle Java-programmører (for en bred definition af Java): Det forstår ikke, helt hvordan virtuelle metoder og generiske typer fungerer i forbindelse med nedarvning, så de holder sig til patterns, de har set virke -- indtil de lige pludselig ikke gør det.

Dette er ikke en kritik af Javaprogrammører, men en kritik af hele grundideen om, at nedarvning og dynamiske metodekald er den foretrukne måde at strukturere programmer på. Det bliver endnu værre, når man begynder at bruge reflection, fordi objekthierarkier viser sig ikke at være tilstrækkelige til generisk programmering. Reflection er ekstremt "leaky" og modvirker alle former for abstraktion. Noget lignende kan siges om aspects.

Ivan Skytte Jørgensen

File descriptors abstraherer væk fra om noget er en fil, pipe, socket, en rå disk, osv. Og abstraktionen fungerer rimeligt, men den lækker aligevel nogle steder:
- man kan ikke lave select() på en fil Hvorfor må jeg ikke lave select(...read) på en fil for at vågne når nogen har appendet til den?
- sendfile() skal have en fil som source-descriptor. Og der findes ingen recvfile() API.Hvorfor det? Det ville da ellers være fikst hvis det var et API til at kopiere x bytes mellemto descriptors
- TCP OOB data kan forekomme på TCP socket descriptors.

Det er ikke noget stort problem, men dog aligevel et irritationsmoment.

Troels Henriksen

  • man kan ikke lave select() på en fil Hvorfor må jeg ikke lave select(...read) på en fil for at vågne når nogen har appendet til den?

Det er nok fordi der ikke er et oplagt svar på hvad der skal ske hvis filen trunkeres, eller der skrives i midten af den (eller den udvides, for den sags skyld). Man kunne selvfølgelig bare vælge hvad der skal ske, men valget vil nok føles lidt vilkårligt.

Jonas Høgh

Det er min opfattelse, at dette er tilfældet for 99% af alle Java-programmører (for en bred definition af Java): Det forstår ikke, helt hvordan virtuelle metoder og generiske typer fungerer i forbindelse med nedarvning, så de holder sig til patterns, de har set virke -- indtil de lige pludselig ikke gør det.


Blander du ikke to ting sammen her? Måder at konstruere abstraktioner og disses utætheder har vel ikke nødvendigvis altid noget med hinanden at gøre. Vi kan godt blive enige om at det er sejere at laver super abstrakt og generisk Haskell-kode, der fx mapper en funktion over en instans af Functor-typeklassen, end at skrive et klassehierarki i Java. Det ændrer bare ikke på, at man kan skabe en instans af abstraktionen, der brænder sammen på kørselstidspunktet, fx hvis din functor er IO-monaden, og din fil ikke er tilgængelig.

Ivan Skytte Jørgensen

Tjo, det er heller ikke noget stort problem. Det eneste sockets og filer i praksis har til fælles er close(), read() og write(). Man kunne så overveje om de i det hele taget begge skulle bruge samme namespace (descriptors), men jeg har arbejdet med OS/2 hvor file handles og socket descriptors ikke var fælles, og det var heller ikke ideelt. Eller i den anden ende af skalaen: win32, hvor filer, mutexes, sockets, ...altsammen er handles, som dog ikke opfører sig helt ens.

Jesper Louis Andersen

Der er altid et sted en abstraktion ender med at knække:

  • Den rene Haskell-funktion tager ikke højde for hukommelsesforbrug
  • Du kan skrive klassehierarkier der bryder med Liskov's substitutionsprincip
  • Du kan definere en instans af Functor eller Monad som ikke er det i Haskell

Og så videre.

Men gode abstraktioner er dem der lader dig beskrive et problem mere præcist end ellers, så du kan arbejde på et højere niveau end normalt. Og gode abstraktioner lader dig modularisere programmet, så dele kan udskiftes løbende, eller udvides uden at skulle lave tværgående omskrivninger.

Det har altid været min overbevisning at transparente systemer er dem der virker.

Hvis du ikke kan ad-hoc forespørge i det (kørende) system og pakke en abstraktion ud, så har du meget lille chance for at håndtere de fejl der gemmer sig. Rigtigt gode systemer er designet sådan at de har selvafslørende dele, hvor de på den ene eller den anden måde oplyser om at de er ved at køre skævt.

Du laver abstraktionen for at undgå denne udpakning til dagligt. Og den gode abstraktion er den hvor det er sjældent du skal gøre det. Men jeg vover at påstå at hvis du bruger et bibliotek uden at nogen i projektet kender dets interne opbygning, så arbejder du med en unødig risiko i dit projekt. Der er simpelthen for mange uskrevne regler om hvad der er forventet anvendelse, og bryder man med en af de regler, så går det som regel galt i det lange løb.

Torben har iøvrigt ret i at aspects er ualmindeligt dårligt som en programmeringsredskab. Men til ad-hoc sporing i systemer er det nok det værktøj jeg anvender mest i mit daglige arbejde når der skal findes fejl (se Erlang's tracing, dtrace(1) i Illumos/FreeBSD/OSX).

Mikkel Lauritsen Blogger

Det er min opfattelse, at dette er tilfældet for 99% af alle Java-programmører (for en bred definition af Java): Det forstår ikke, helt hvordan virtuelle metoder og generiske typer fungerer i forbindelse med nedarvning, så de holder sig til patterns, de har set virke -- indtil de lige pludselig ikke gør det.

Interessant nok er Alan Knight primært Smalltalk-programmør, og generelt tror jeg ikke, at det med hvorvidt OO er en hensigtmæssig måde at bygge software på er et spørgsmål, som specielt gør sig gældende for Java. Men det er måske også det, der skal lægges i det med "bred definition" :-)

Personligt synes jeg heller ikke, at det er nedarvning og dynamisk dispatch, som giver de fleste problemer med lækkende abstraktioner. I mit indledende eksempel er det muligt, at det meget isoleret set er nedarvning, som har gjort det muligt at returnere en subclass af Date, som har uhensigtsmæssige egenskaber, men man kunne have oplevet tilsvarende problemer i alle mulige andre typer af sprog. Problemet med at maskinens hukommelse ikke er et uniformt array af bytes kræver ikke engang et højniveausprog for at vise sig.

Troels Henriksen

Problemet med at maskinens hukommelse ikke er et uniformt array af bytes kræver ikke engang et højniveausprog for at vise sig.

Problemet med denne abstraktion er dog primært ydelsesbaseret, og hvis det er målestokken, så er der næsten ingen abstraktioner der holder vand. Hvis du ikke ved nøjagtigt hvad der sker, så ved du ikke hvordan det yder, men som ofte er man også ligeglad.

Klavs Klavsen

Jeg vil egentlig mene at disse steder, hvor der er ambiguitet, netop afspejler steder hvor OO metodikken IKKE er den bedste løsning, til at sikre kode der er nemt af fejlfinde og udvikle i (uden overraskelser). Jeg vil næsten påstå at OO bedst er brugt til datastrukturer (ordrer, varer, indkøbskurv mv.) og funktioner er bedre til interaktion med "omverdenen" - sådan når det gælder at sikre at koden bliver nem for alle at gå til, og så burde man netop kunne undgå disse "leaky abstractions". Og når der så kommer multipel arv ind i billedet og mudrer billedet, ligesom Mikkels Date eksempel.. så synes jeg bare det viser områder hvor OO IKKE forbedrer kodekvaliteten, og det bliver et eksempel på at alle problemer er søm, fordi man kun står med en hammer.

Mange virker til at have glemt en af de største kvaliteter ved god kode (efter min mening naturligvis ;) - nemlig at den er let forståelig og ikke "leder i uføre".. de højere niveau sprog blev vel primært opfundet i et forsøg på at gøre os mere effektive, ikke for at anstrenge os endnu mere :)

Ikke at man ikke kan lave gode abstraktioner for filhåndtering mv.. men man skal dælme passe på hvor meget funktionalitet man så forsøger at udstille og abstrahere væk..

Man fristes til at sige man glemmer at gøre det KISS :)

Mikkel Lauritsen Blogger

Problemet med denne abstraktion er dog primært ydelsesbaseret, og hvis det er målestokken, så er der næsten ingen abstraktioner der holder vand. Hvis du ikke ved nøjagtigt hvad der sker, så ved du ikke hvordan det yder, men som ofte er man også ligeglad.

Well... Nu ved jeg ikke, hvem der bruger den kode, du skriver, men for mit vedkommende er det stik modsat. Jeg er aldrig ligeglad med de performancemæssige aspekter af de abstraktioner, som jeg betjener mig af.

Når abstraktioner giver anledning til at koden giver forkerte resultater (evt. ved at den dørker, som i mit oprindelige eksempel), så opdages det næsten altid før man leverer til kunden, hvorimod de performancerelaterede problemer forårsaget af lækkende abstraktioner har en kedelig tendens til først at dukke op efter et stykke tid efter koden er sat i produktion. De gør i øvrigt kunderne svært utilfredse, og det kan være dyrt at rette op på problemerne, fordi det kræver strukturelle ændringer i koden.

Og jeg tror ikke, at jeg er den eneste, som har det sådan. Tag fx igen Polsag-historien - applikationen giver sikkert de rigtige resultater, men er bare ubrugelig i praksis, fordi alt kører som en gigtramt skovsnegl indefrosset i en gletcher.

Troels Henriksen

Well... Nu ved jeg ikke, hvem der bruger den kode, du skriver, men for mit vedkommende er det stik modsat. Jeg er aldrig ligeglad med de performancemæssige aspekter af de abstraktioner, som jeg betjener mig af.

Jeg er PhD-studerende i kode-optimering og high-performance-computing, så noget nær mit eneste fokus er køretidsydelse. Fra mit synspunkt bruger alle (inklusive mig selv) abstraktioner der giver et enormt mudret billede af køretidsydelsen. Insisterer du ikke selv på kun at bruge sprog der kører oven på en virtuel maskine? Det er da i dén grad en (nyttig og værdig) abstraktion der besværliggør forståelsen af hvad den fysiske maskine egentlig ender med at bruge sin tid på.

Jonas Høgh

Og jeg tror ikke, at jeg er den eneste, som har det sådan. Tag fx igen Polsag-historien - applikationen giver sikkert de rigtige resultater, men er bare ubrugelig i praksis, fordi alt kører som en gigtramt skovsnegl indefrosset i en gletcher.

Det gør den (angiveligt) fordi den laver alt for mange databaseroundtrips, ikke fordi koden ikke er optimeret i forhold til registerallokering, cachelokalitet o.a. der er mange størrelsesordener billigere end et enkelt netværkskald. Min erfaring er i hvert fald, at man i en forretningsapplikation meget meget sjældent behøver at tænke over performance, når man bare har styr på hvordan databasen performer.

Mikkel Lauritsen Blogger

Det gør den (angiveligt) fordi den laver alt for mange databaseroundtrips, ikke fordi koden ikke er optimeret i forhold til registerallokering, cachelokalitet o.a. der er mange størrelsesordener billigere end et enkelt netværkskald. Min erfaring er i hvert fald, at man i en forretningsapplikation meget meget sjældent behøver at tænke over performance, når man bare har styr på hvordan databasen performer.

110% enig. Man skal være endog meget dygtig for at kunne skrive kode, som tager længere tid at eksekvere end et databaseroundtrip.

Faktisk kan det godt være, at det er regel #1 for at undgå problemer med lækkende abstraktioner i forretningsapplikationer: sørg for, at du altid har styr på, hvad de databasemæssige konsekvenser af din kode er.

Mikkel Lauritsen Blogger

Jeg er PhD-studerende i kode-optimering og high-performance-computing, så noget nær mit eneste fokus er køretidsydelse.

Nu er jeg lidt forvirret. Du skrev:

Hvis du ikke ved nøjagtigt hvad der sker, så ved du ikke hvordan det yder, men som ofte er man også ligeglad.

Med min umiddelbare læsning fremstår "ligeglad med" og "eneste fokus er" som hinandens modsætninger?

Min pointe var (er), at "man" gennemgående ikke kan tillade sig at være ligeglad med de performancemæssige aspekter af de abstraktioner, man betjener sig af. Hvad det så mere præcist er for overvejelser man skal gøre sig er selvfølgelig stærkt afhængigt af arten af den kode, man arbejder på - sådan noget med at man skal tænke over, hvordan man har en sparse matrix liggende i hukommelsen for at få maksimal båndbredde er ret ligegyldigt for mig, men det er det ikke for folk, som arbejder med HPC.

Insisterer du ikke selv på kun at bruge sprog der kører oven på en virtuel maskine?

Insisterer og insisterer... Det er det, som giver mig mest bang for the buck, men hvis der fandtes et sprog uden underliggende VM, som gjorde mig mere produktiv, så ville jeg skifte så hurtigt som muligt.

Det er da i dén grad en (nyttig og værdig) abstraktion der besværliggør forståelsen af hvad den fysiske maskine egentlig ender med at bruge sin tid på.

Ja, det er bestemt nyttigt, og ja, den gør det til dels svært at gennemskue, hvad der præcist sker nedenunder. Og det er heldigvis ikke noget, som betyder det store for mig i dagligdagen, jfr. det med at et databaseroundtrip alligevel tager så lang tid.
FWIW er JVM'en i øvrigt ret god til at undgå alt for ubehagelige overraskelser, selv om folk, der gerne vil have en høj framerate fra deres Android-kode nok synes, at pauser fra garbage collection er et fint eksempel på en uhensigtsmæssig læk fra abstraktionen :-)

Jesper Louis Andersen

Min pointe var (er), at "man" gennemgående ikke kan tillade sig at være ligeglad med de performancemæssige aspekter af de abstraktioner, man betjener sig af.

Kun hvis abstraktionen er en del af din beregningskerne. Uhyggeligt meget kode har det med at falde udenfor det behov i praksis. Produktivitet er også at kunne gennemskue hvornår man bare skal hive en abstraktion ind fra højre, velvidende at den koster noget i systemet. Det gør at man kan bruge tiden et andet sted i sit system, hvor det har større effekt.

Case in point: Erlang kører via en bytekodefortolker uden JIT. Det gør den ca. 10 gange langsommere end tilsvarende C kode og en 7-8 gange langsommere end tilsvarende Java eller OCaml kode. Men et typisk Erlang-program, når du profiler det, bruger kun ca. 20% af tiden i emulator-loopet i fortolkeren, så selv hvis vi eliminerer dette er vores program kun blevet maksimalt 20% hurtigere i praksis. Til gengæld kan jeg vel skrive kode 10-20 gange hurtigere i Erlang-abstraktionen, så jeg kan bruge tiden andetsteds, mere fornuftigt.

Mikkel Lauritsen Blogger

Kun hvis abstraktionen er en del af din beregningskerne. Uhyggeligt meget kode har det med at falde udenfor det behov i praksis. Produktivitet er også at kunne gennemskue hvornår man bare skal hive en abstraktion ind fra højre, velvidende at den koster noget i systemet. Det gør at man kan bruge tiden et andet sted i sit system, hvor det har større effekt.

Nu begynder jeg måske at kløve hår, men det, jeg snakker om, er lækkende abstraktioner - altså der, hvor abstraktionerne har indre detaljer, som man egentlig helst skulle kunne se bort fra, men hvor det giver problemer at gøre det. Og hvis de detaljer er af performancemæssig art kan det være et endnu større problem end hvis de medfører, at ens kode giver et forkert resultat.

Den, at Erlang-fortolkeren er langsom, er ikke i min verden en lækkende abstraktion. Det er fuldt ud forstået, at den er langsom, og det er den konsekvent.

Mit standardeksempel på den farlige lækkende abstraktion er ORM-frameworket, som laver en sædvanlig getter-metode om til at lazy-loade, så det, der giver indtryk af bare at være at følge en sædvanlig objektreference, lige pludselig bliver til at der fortages et databaseroundtrip. Sådanne detaljer kan man (desværre) ikke være ligeglad med.

Jesper Tholstrup

I sproget D er brugen af UFCS meget udbredt for at undgå at biblioteks-udviklere kontinuerligt finder på nye sub-klasser som til sidst giver et ret rodet billede af hvilke objekter man arbejder med. Om det ville løse det aktuelle problem kan jeg ikke svare på, men generelt skulle effekten være at klassehierarkiet kan forsimples.

Løsningen i D er inspireret fra Scott Meyers [1] og består i grunden af at kompileren omskriver "foo(a)" til "a.foo()" hvis "foo(a)" ikke findes. Jeg kan egentlig godt lide konceptet.

Den aktuelle tilfælde lyder da i øvrigt som om Liskovs substitutionsprincip er brudt - eller hvad?

1: http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/1844...

Nikolaj Brinch Jørgensen

Mit standardeksempel på den farlige lækkende abstraktion er ORM-frameworket, som laver en sædvanlig getter-metode om til at lazy-loade, så det, der giver indtryk af bare at være at følge en sædvanlig objektreference, lige pludselig bliver til at der fortages et databaseroundtrip. Sådanne detaljer kan man (desværre) ikke være ligeglad med.


Men er det en leaky abstraction? Det er jo dokumenteret at det er sådan det virker, og du kan jo lave det om - du kan jo deklarativt ændrer dette. Både generelt og i enkelte tilfælde (det er jo en funktionalitet man gerne vil have i nogle sammenhænge - en del faktisk).
Det er korrekt at der er et mismatch mellem Object model og Relational model, og at en ORM derfor vil være leaky, men det er vel et forstået problem for dem som arbejde med omådet?

Pointen er vel, at det er nødvendigt at sætte sig ind i hvad man arbejde med, for at kunne gøre arbejdet ordenligt. http://martinfowler.com/bliki/OrmHate.html

Jonas Høgh

Men er det en leaky abstraction? Det er jo dokumenteret at det er sådan det virker, og du kan jo lave det om - du kan jo deklarativt ændrer dette. Både generelt og i enkelte tilfælde (det er jo en funktionalitet man gerne vil have i nogle sammenhænge - en del faktisk).
Det er korrekt at der er et mismatch mellem Object model og Relational model, og at en ORM derfor vil være leaky, men det er vel et forstået problem for dem som arbejde med omådet?


Helt enig. Og en ting er at mange bruger ORM naivt og undlader at tænke over at mange lazy loads ofte performer dårligt. En anden ting er, at det ORM Mikkel her har fat i, må være meget patologisk, hvis det er nødvendigt at generere proxy-typer af primitive værdityper som datoer...men måske lever jeg bare i lykkelig uvidenhed om rædslerne i java.util.Date?

Nikolaj Brinch Jørgensen

java.util.Date


er en rædsel, men der er heller ikke nogen grund til at bruge den. Joda eller Java 8 date/time er langt at foretrække og kan sagtens benyttes med Hibernate (der kan være gamle legacy systemer hvor det muligvis ikke kan lade sig gøre, men så må man gribe til refactoring).
Datoer er iøvrigt ikke primitive typer. java.util.Date er ikke en dato, men et tidspunkt, og både datoer og tidspunkter er temmeligt komplicerede datatyper, hvilket java.util.Date og dens rædsler også er vidner om.

Hibernate (og andre ORMs) benytter i udstrakt grad proxies. Det er ikke nødvendigvis forkert, og det ville være svært for dem at leve op til kontrakten, hvis ikke de gør. Men som alt andet software kan der jo altså være fejl i implementationen. Det gør ikke nødvendigvis en abstraktion leaky, det afslører blot en bug.

Man skal også huske på at i visse tilfælde vil en join (for eager fetching) ikke producere de korrekte resultater, og i nogle tilfælde heller ikke være hurtigere end lazy fetching. Det kommer helt an på hvordan man har skruet sit skema sammen. Dvs. hvilke use cases skema er optimeret for.

Mikkel Lauritsen Blogger

Men er det en leaky abstraction? Det er jo dokumenteret at det er sådan det virker, og du kan jo lave det om - du kan jo deklarativt ændrer dette. Både generelt og i enkelte tilfælde (det er jo en funktionalitet man gerne vil have i nogle sammenhænge - en del faktisk).

Joel Spolskys lov om lækkende abstraktioner siger, at alle nontrivielle abstraktioner er lækkende - men ja, jeg synes, at det (og ORM-frameworks generelt) er en særdeles lækkende en af slagsen. I det omfang at man ved, hvordan tingene virker, er alting jo klart, men det er bare min erfaring, at netop ORM-frameworks er i særklasse svære for udviklere at have med at gøre.

Et andet godt eksempel fra det ORM-framework jeg slås med til daglig er det at føje et objekt til en persistent collection. Når man gør det, så populerer frameworket først collectionen, så man altså ender med at lave et potentielt meget stort antal objektinstanser, uanset om man faktisk nogensinde tilgår dem eller ej. Netop den abstraktion er så lækkende, at den er tæt på at gøre mere skade end gavn.

Jonas Høgh

Et andet godt eksempel fra det ORM-framework jeg slås med til daglig er det at føje et objekt til en persistent collection. Når man gør det, så populerer frameworket først collectionen, så man altså ender med at lave et potentielt meget stort antal objektinstanser, uanset om man faktisk nogensinde tilgår dem eller ej. Netop den abstraktion er så lækkende, at den er tæt på at gøre mere skade end gavn.

Ja det er irriterende, men det har IMO meget lidt at gøre med en lækkende abstraktion. Abstraktionen er at du arbejder med en almindelig collection af objekter. Sådan nogle ligger normalt og fylder i hukommelsen :). Det er kun fordi du ved hvad der ligger bag abstraktionen, at du sukker efter en ORM, der "bare kan generere en simpel INSERT-statement".

Nikolaj Brinch Jørgensen
Jonas Høgh

Datoer er iøvrigt ikke primitive typer.

Undskyld, jeg mente ikke Javas snævre definition heraf, blot at det er en (forholdsvis) simpel Value Type som defineret i Eric Evans :)

Mener du virkelig at Hibernate bruger proxyer på "feltniveau", jeg troede kun det var på relationer fra en entitet til en anden, eller fra en entitet til en collection?

Nikolaj Brinch Jørgensen

Mener du virkelig at Hibernate bruger proxyer på "feltniveau", jeg troede kun det var på relationer fra en entitet til en anden, eller fra en entitet til en collection?


Nej det mener jeg selvfølgelig ikke. My bad :-) Det er som du siger. Entiteter er repræsenteret vha. proxies. Men hvis man er helt ude i hampen kan man jo erklære en java.util.Date som en entitet :-)

Log ind eller opret en konto for at skrive kommentarer