At debugge indkapslede data

Objektorienteret programmering er selvfølgelig et af mine værktøjer, men jeg har aldrig været specielt overbevist. Derfor har Perls objekt-model egentlig passet mig godt. Et objekt i Perl er bare en reference til en datastruktur, der er markeret med et pakkenavn. En metode er så bare en normal funktion i denne pakke der tager objektet som første argument.

Oftest bruger man referencer til et associativt array, som man så bruger til at holde objektets data. Dette giver alle mulighed for at inspicere og pille i objektet. Efter at have læst Perl Best Practices af Damian Conway er jeg lidt blevet overbevist om at det ikke altid er optimalt. Damian Conways alternativ kaldes Inside-Out objekter, der sikrer en god indkapsling.

Et Inside-Out objekt består ikke af en reference til objektets data, men kun til et objekt-id. Metoder er så closures der indeholder associative arrays eller lister hvori data for alle objekter af typen bliver gemt. Da data så kun er tilgængelige i metoder som defineres i sammen kode-blok giver dette en næsten perfekt indkapsling - det er kun mine metoder der kan tilgå data, hverken nedarvede klasser og end ikke min debugger kan læse eller skrive data uden om mine metoder.

Men jeg kan altså godt lide at kunne se hele tilstanden af mit system når jeg debugger system-integration. Så kære OO-hajer, hvis I stadigvæk læser med, hvordan opnår I både data-indkapsling uden at fraskrive jer muligheden for at debugge?

Snyder I og laver nogle metoder der bryder indkapslingen eller er jeres debuggere bare stærker end min (eller jeres indkapsling svagere end min)' Eller sørger I bare på en anden måde for at I kan debugge uden at inspicere objekters tilstand'

Kommentarer (15)
sortSortér kommentarer
  • Ældste først
  • Nyeste først
  • Bedste først
Torben Mogensen Blogger

Efter min mening er skjult opdaterbar tilstand en kilde til flere problemer end det løser, så jeg undgår det så vidt muligt.

Peter nævner problemer med debuggere, som ikke kan se den skjulte tilstand, men det samme problem genfindes også i test (f.eks. unit test). I unit test aktiverer du dine metoder med diverse argumenter og ser om resultatet er det forventede. Men hvordan ser ud, om den skjulte tilstand er som forventet? Og hvis du tilføjer metoder til at læse den skjulte tilstand under test, så har du i dine test allerede fastlagt en specifik implementering (nemlig formen af den skjulte tilstand), så ellers korrekte ændringer af implementeringen fejler i unit testningen.

Jeg siger ikke, at abstraktion er dårligt -- jeg bruger hele tiden abstrakte typer, men det bør være repræsentation og implementering og ikke tilstanden, der er skjult.

  • 0
  • 0
Peter Makholm Blogger

Måske ville det være lidt på sin plads at diskutere hvilke problemer vi overhovedet forsøger at løse.

Man kan måske nævne en række tekniske problemer med at forskellige klasser i klassehierarkiet træder hinanden over tæerne, men det tror jeg kan løses med mildere midler.

Det virkelige problem er rent socialt, i min verden findes der ikke noget der hedder 'udokumenterede features'. Så hvis der er mulighed for at opnå noget ved at pille direkte i objektets tilstand, så er der på et tidspunkt nogen der gør det. Og derefter er det mig der ødelægger noget, hvis jeg sidenhen ændrer implementationen på en ikke-kompatibel måde.

Jo dybere magi der skal til for at bryde med den dokumenterede grænseflade jo bedre. Det holder simpelthen min røv fri i tilfælde af fremtidige udvidelser.

  • 0
  • 0
Peter Makholm Blogger

Du har ret i at lige så snart jeg er 'inde i et metodekald', så kan jeg godt inspicere objektets tilstand.

Forestil dig at du har ti linjer kode og der et eller andet sted er noget der får et af fire objekter til at indeholde noget garbage. Så vil det være rart at kunne udføre første linje kode, teste at alle objekter har en fornuftig tilstand, udføre næste linje, tjekke igen og så videre. På den mpde vil jeg kunne skære mig ned til at det er en enkelt linje kode der gør noget uventet og så vil jeg kunne steppe ned i de enkelte bneregningsskridt i den linje.

For at kunne løse mit problem med et simpelt breakpoint ville jeg skulle introducere nogle dummy-metoder alle de steder hvor jeg kunne tænkes at ville aflæse objektets interne tilstand.

  • 0
  • 0
Bjarke Walling

I Python er et objekts data som default fuldt tilgængeligt fra andre metoder. Det er mange gange fint nok, men det kan være en fordel at andre folk i et større projekt, der skal bruge ens klasse, ikke kan pille i den indre tilstand. F.eks. tråd-objekter, locks, o.lign. med fare for at introducere deadlocks. I Python har man muligheden for at sætte to underscore foran sine variabelnavne, hvorefter de kun er tilgængelige fra klassens egne metoder. Problemet er bare at PDB (Python Debugger) heller ikke kan tilgå dem. Indtil nu har jeg løst det ved en simplere form for debugging med at udskrive variabler til skærmen, men det kunne være smart at debuggeren havde read-only-adgang til private data.

  • 0
  • 0
Kasper Sørensen

Nu forstår jeg ... en måde du kunne gøre det på var at indkapsle yderligere. Man kan sige at det du beskriver er et tilfælde hvor mange metoder påvirker samme data og så er indkapslingen jo ikke særlig omsiggribende. Så mit forslag til dit "indkapslings-problem" er sjovt nok at indkapsle yderligere: Lav eks. en metode ala (java syntax eksempel)

public void setMyDataStructure(MyDataStructure newData);

Siden alt datamanipulation nu vil gå igennem denne metode kan du nøjes med at sætte et break point dér.

En anden måde som er foreslået før er at benytte unittests, hvilket jeg også gerne vil reklamere for. Jeg læste den anden dag en eller anden udtale at hvis man kender alle éns debugger shortcuts, så unittester man for lidt :) Ikke at jeg er 100% enig, men jeg kan godt lide tanken om at man finder fejlene i en test - og hvis man KAN det skyldes det nok også et gennemskueligt design (med tilstrækkelig indsigt i det indkapslede), siden testen kan finde ud af at bruge det til noget.

  • 0
  • 0
Peter Mogensen

De sprog, hvor privat data ikke er tilgængelig for andre objekter via sproget selv har da som oftes også en debugger, der benytter lidt bag-om-ryggen tricks.

C++ er nok et lidt dårligt eksempel, men hvis vi nu udelukker fuskende pointer-operationer, så kan du jo heller ikke tilgå privat data der. Men tror du ikke debuggeren faktisk bruger fuskende pointer-operationer for at vise din private data?

  • 0
  • 0
Peter Makholm Blogger

Bag om ryggen-tricks er helt i orden. Sprog der har en fast objekt-model burde det også være ret ligetil at definere disse bag om ruggen-operationer. Med Python burde det være muligt at lave en debugger der har adgang til private data, hvis de bare angives syntaktisk som et specielt navnerum.

Perl5 er lidt anderledes, for det har ikke nogen egentlig objekt-model. Kun noget klamp af referencer der bliver "markeret" med et klassenavn og så en masse syntaktisk sukker. Så må man anvende nogle bag om ryggen-tricks så vores data ikke lækker, og den slags kan debuggeren jo ikke kende til.

  • 0
  • 0
Peter Makholm Blogger

Der er faktisk en simpel løsning...

Det kræver at jeg sætter et breakpoint inde i en metode, men når jeg først er derinde kan jeg gemme referencer til de data jeg ønsker at have adgang til. Derefter kan jeg tilgå data gennem denne reference selv når jeg ikke er inde i metode-kaldene.

Det vil rimelig let kunne opfylde mit ønske om at kunne udskrive før og efter-værdier når jeg skal debugge 10 linjer kode ofr at se hvornår der går noget galt.

  • 0
  • 0
Peter Mogensen

Nej, den slags kan debuggeren jo ikke kende til. Den kan jo af gode grunde kun kende til Perl5s indbyggede OO-understøttelse, der dybest set kun består af "bless" funktionen og en lookup-metode for at finde subs baseret på bless/ISA.

Men det er jo ikke anderledes end i C++. C++-debuggeren kan heller ikke erkende hvis du implementerer dine OO-abstraktioner på en anden måde end den indbyggede.

  • 0
  • 0
Jacob Sparre Andersen

Jeg mener at i nogle debuggere kan man kalde et objekts metoder, når man er stoppet ved et break-point. Kunne det være en idé at have en »debug«-metode for hver af sine klasser, der blot skriver objektets interne tilstand ud?

Hvis man fusker med implementationen af »debug«-metoden, så den kun gør noget, hvis programmet er oversat til fejlsøgning, så er det vel ikke specielt sandsynligt at folk finder på at misbruge metoden.

  • 0
  • 0
Peter Makholm Blogger

Asserts er da netop underlagt de problemer jeg beskriver: Man kan kun lave asserts på informationer man har adgang til, så det er kun i objektets metoder man kan lave asserts der tester på objektets interne tilstand.

Det giver altså stadigvæk ikke nogen løsning hvis klassen selv er lavet til at kunne behandle en bred række tilfælde, mens jeg helt lokalt har en forventning om at objektets tilstand har en mere begrænset antal værdier.

Og jeg kan slet ikke lave asserts på at to objekter er i en konsistent intern tilstand i forhold til hinanden. Så kan man nok argumentere for at der er et designproblem hvis man har den slags koblinger ikke-eksplicitte koblinger.

  • 0
  • 0
Log ind eller Opret konto for at kommentere