Objektorienterad programmering i Racket

Relevanta dokument
Objektorienterad programmering i Racket

TDDC74 Programmering: Abstraktion och modellering Dugga 3, kl 14 16, 25 mars 2015

TDDC74 Programmering: Abstraktion och modellering Tentamen, lördag 29 augusti 2015, kl 8 12

TDDC74 FÖRELÄSNING 9 ANDERS MÄRAK LEFFLER IDA/HCS

TDDC74 Programmering, abstraktion och modellering. Tentamen

Introduktion. Klasser. TDP004 Objektorienterad Programmering Fö 2 Objektorientering grunder

TDDC74 Programmering, abstraktion och modellering. Tentamen

2I1049 Föreläsning 5. Objektorientering. Objektorientering. Klasserna ordnas i en hierarki som motsvarar deras inbördes ordning

Kort om klasser och objekt En introduktion till GUI-programmering i Java

TDDC74 - Lektionsmaterial C

TDDC74 Programmering: Abstraktion och modellering Dugga 3, kl 8 10, 7 april 2016

TDDC74 Lab 04 Muterbara strukturer, omgivningar

Laboration 1: Figurer i hierarki

TDDC74 Programmering: Abstraktion och modellering Datordugga 2 - exempel

TDDC74 Programmering: Abstraktion och modellering Tentamen, onsdag 9 juni 2016, kl 14 18

TDDC74 Programmering: Abstraktion och modellering Dugga 2, kl 8 10, 5 mars 2015

Objektorienterad programmering. Grundläggande begrepp

Arv. Fundamental objekt-orienterad teknik. arv i Java modifieraren protected Lägga till och modifiera metoder med hjälp av arv Klass hierarkier

TDDC74 Programmering, abstraktion och modellering DUGGA 3

Objektorientering: Lagring och livstid

TDDC74 Programmering: Abstraktion och modellering Datortenta

Dagens program. Programmeringsteknik och Matlab. Objektorienterad programmering. Vad är vitsen med att ha både metoder och data i objekten?

TDDC74 Programmering: Abstraktion och modellering Datortenta , kl 14-18

TDDC74 Programmering: Abstraktion och modellering Datortenta , kl 14-18

TDDC74 Programmering, abstraktion och modellering. Tentamen

TDDC74 Programmering: Abstraktion och modellering Datortenta , kl 08-12

TDDC74 Programmering: Abstraktion och modellering Tenta, kl 14 18, 11 juni 2014

TDDC74 Programmering, abstraktion och modellering. Tentamen

Imperativ programmering. Föreläsning 4

Objektorientering: Lagring, räckvidd och livstid

Objektorienterad Programmering (OOP) Murach s: kap 12-16

Inkapsling tumregler. Åtkomstmodifikatorer, instantiering, referenser, identitet och ekvivalens, samt klassvariabler. public och private

Föreläsning 8 - del 2: Objektorienterad programmering - avancerat

Föreläsning 4. Klass. Klassdeklaration. Klasser Och Objekt

Lösningsförslag. TDDC74 Programmering: Abstraktion och modellering. Dugga 3 (provkod TEN1), Tid: kl 14-16, Datum:

Innehåll. dynamisk bindning. och programmering CRC) u Arv, polymorfi och

TUTORIAL: KLASSER & OBJEKT

Inledande programmering med C# (1DV402) Tärningarna ska kastas

TDDC74 Programmering: Abstraktion och modellering Tentamen, lördag 27 augusti 2016, kl 8 12

TDDD78 Objektorientering: Lagring och livstid

public och private Obs: private inte skyddar mot access från andra objekt i samma klass.

I STONE. I Variabler, datatyper, typkonvertering. I Logiska och matematiska uttryck. I Metoder-returvärde och parametrar. I Villkorssatser if/else

DAT043 - Föreläsning 7

725G61 - Laboration 7 Implementation av ett API. Johan Falkenjack

Grundläggande programmering med C# 7,5 högskolepoäng

Börja med att kopiera källkoden till din scheme-katalog (som du skapade i Laboration 1).

"Är en"-relation. "Har en"-relation. Arv. Seminarium 2 Relevanta uppgifter. I exemplet Boll från förra föreläsningen gällde

Idag. Javas datatyper, arrayer, referenssemantik. Arv, polymorfi, typregler, typkonvertering. Tänker inte säga nåt om det som är likadant som i C.

F8 - Arv. ID1004 Objektorienterad programmering Fredrik Kilander

TDDC74 Programmering: Abstraktion och modellering Dugga 2, , kl 14-16

Tentamen ID1004 Objektorienterad programmering October 29, 2013

Dagens föreläsning Programmering i Lisp. - Bindning av variabler (avs 14.6) fria variabler statisk/lexikalisk och dynamisk bindning

Projekt i programmering 1 (ver 2)... 2 Projektidé... 2 Planering... 2 Genomförande... 2 Testning och buggar... 3 Utvärdering... 3 Planering...

Abstrakta Klasser 2. Kodning är bara en liten del i programvaruutvecklingen 6% 1% 6% Abstrakta Klasser - deklaration. Programutveckling sker i faser

Objektorienterad Programmering (TDDC77)

Metoder (funktioner) Murach s: kap Winstrand Development

TDDC74 Programmering: Abstraktion och modellering Dugga 2, , kl 17-19

DIAGNOSTISKT PROV. Tid. Hjälpmedel. Antaganden. Rättning. Övrigt. Diagnostiskt Prov. Klockan Inga

Objektorienterad Programkonstruktion. Föreläsning 4 8 nov 2016

Objektorienterad programmering

Administrativt. Programmeringsteknik för I1. Dagens program. Objektorienterad programmering

Föreläsning 16 Arv. Jan Lönnberg T Grundkurs i programmering

Outline. Objektorienterad Programmering (TDDC77) Att instansiera en klass. Objekt. Instansiering. Åtkomst. Abstrakt datatyp.

Objektorienterade programmeringsspråk. Objektorienterade språk. Den objekt-orienterade modellen. Jämför med icke-oo

TDDC74 Programmering: Abstraktion och modellering Tentamen, onsdag 19 oktober 2016, kl 14 18

2203$( Föreläsning ii - Mer om Java bla this och konstruktorer. Exempel: lampa

Lösningar till Fiktiv Tentamen på kursen. 2D4135 Objektorienterad programmering, design och analys med Java vt2004. Teoridel

Klasser och objekt. Henrik Johansson. August 20, 2008

Statistik över heltal

Föreläsning 5-6 Innehåll. Exempel på program med objekt. Exempel: kvadratobjekt. Objekt. Skapa och använda objekt Skriva egna klasser

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 2 Jonas Lindgren, Institutionen för Datavetenskap, LiU

Föreläsning 5-6 Innehåll

Static vs Dynamic binding Polymorfism. Objekt-orienterad programmering och design (DIT953) Niklas Broberg, 2018

Classes och Interfaces, Objects och References, Initialization

Objektorienterad programmering i Java I

TDDC74 Lab 02 Listor, sammansatta strukturer

n Detta för att kunna koncentrera oss på n Tal: number? n Symboler: symbol? n Strängar: string? n Tecken: char? n Boolskt: boolean?

TDDC74 Programmering: Abstraktion och modellering Dugga 1, kl 14-16

Introduktion till objektorientering. Vad är objektorientering egentligen? Hur relaterar det till datatyper? Hur relaterar det till verkligheten?

Föreläsning 8. Arv. Arv (forts) Arv och abstrakta klasser

Introduktion till Datalogi DD1339. Föreläsning 2 22 sept 2014

Introduktion till arv

1 Klasser och objektorientering Vad är objektorientering?

Grundläggande programmering, STS 1, VT Sven Sandberg. Föreläsning 14

Arv: Fordonsexempel. Arv. Arv: fordonsexempel (forts) Arv: Ett exempel. En klassdefinition class A extends B {... }

Målen med OOSU. Objektorienterad programmering. Objektorienterad programmering. Karlstads Universitet, Johan Öfverberg 1

Kopiering av objekt i Java

Objekt, klasser. Tillstånd Signatur Kommunikation Typ. Fält, parametrar och lokala variabler. Konstruktorer Metoder DAVA15

Laboration 1 - Grunderna för OOP i Java

Typhierarkier del 1 Gränssnitt, ärvning mellan gränssnitt, ärvning mellan klasser

TDDC74 Programmering: Abstraktion och modellering Dugga 1, exempeldugga

Övningsuppgift. Bankkonton. Steg 2. Författare: Mats Loock Kurs: Inledande programmering med C# Kurskod:1DV402

Klasser i Java kan ha metoder och egenskaper. Metoder beskriver funktioner som klassen kan utföra. Egenskaper beskriver innehållet i klassen.

TDDE10 TDDE11, 725G90. Objektorienterad programmering i Java, Föreläsning 3 Erik Nilsson, Institutionen för Datavetenskap, LiU

TDDC74 Programmering: Abstraktion och modellering Dugga 2, Tid: kl 08-10, Datum:

Exempel: Exempel: Exempel: Exempel: $djur=array("ko","katt","älg"); foreach ($djur as $d) { echo $d. " "; } Resultat. ko katt älg

OOP Objekt-orienterad programmering

TDDE10 TDDE11, 725G90/1. Objektorienterad programmering i Java, Föreläsning 2 Erik Nilsson, Institutionen för Datavetenskap, LiU

Java, klasser, objekt (Skansholm: Kapitel 2)

Administrivia. hh.se/db Verónica Gaspes (Kursansvarig) 2 Mattias Enervall (Övningsassistent) Examination. 1 Skriftlig tentamen (betyg)

Transkript:

Objektorienterad programmering i Racket Rasmus Andersson lätt utökat av Anders M. L. Februari 2016 Innehåll 1

1 Inledning Detta kompendium är skrivet som en resurs för kursen TDDC74 Programmering - abstraktion och modellering vid Linköpings universitet. Tanken är att det ska introducera de grundläggande begreppen inom objektorienterad programmering och ge exempel på implementation i Racket. 1.1 Vad detta är, och inte är Kompendiet ska ge tillräckliga kunskaper för att lösa laboration 4 i 2015 års upplaga av kursen (och att börja på ett objektorienterat projekt). Därför utelämnar jag medvetet delar som annars kan anses relevanta. För den som är intresserad av fler detaljer rekommenderar jag Flatt m.fl.[? ] som finns att läsa på http://www.cs.utah.edu/plt/publications/aplas06-fff.pdf. Det bör dock noteras att den är skriven för en (väldigt närliggande) föregångare till Racket. Den ska dock fungera utmärkt även i dagens Racket. Jag kan även rekommendera kapitel 13 i The Racket Guide[? ] http://docs.racket-lang.org/guide/classes.html som till stor del är baserad på den tidigare nämnda artikeln. 1.2 Att läsa detta dokument Vi börjar med att definiera de olika begreppen och de viktiga delarna inom objektorienterad programmering. Därefter följer ett exempel på hur vi skapar klasser och objekt. Om du bara vill ha en mall, kan du gå direkt till detta exempelavsnitt. 2

2 Var är ett objekt? Vi kan, lite slarvigt, se ett objekt som en ihopgruppering av variabler och procedurer som på något sätt hör ihop, och som har en väldefinierad roll i världen. Men det säger kanske inte så mycket. Istället börjar vi närma oss konceptet genom ett exempel på en hantering som verkligen är motsatsen till objektorienterad, och ser hur den kan förbättras. 2.1 Avskräckande exempel Det kan vara bra att ta sig genom hela detta exempel. Vill du bara se slutresultatet, och hur man använder mallar, gå till sista delen. Men det kan vara bra att se lite varnande exempel på vägen, för att förstå varför detta kan vara användbart. 2.1.1 En mängd fristående variabler Låt säga att vi vill konstruera att enkelt bilspel med 10 bilar. Varje bil har ett antal egenskaper som x-position, y-position, x-hastighet, y-hastighet, märke, färg, etc. Vi gör en första, naiv, approximation av detta: (define car-1-x-pos 12) (define car-1-y-pos 3) (define car-1-x-velocity 2) (define car-1-y-velocity 0) (define car-1-brand "VOLVO") (define car-1-color white) (define car-2-x-pos 37) (define car-2-y-pos 3) (define car-2-x-velocity 3) (define car-2-y-velocity 7) (define car-2-brand "SAAB") (define car-2-color red) (define car-3-x-pos 99) (define car-3-y-pos 24) (define car-3-x-velocity 0) (define car-3-y-velocity 0) (define car-3-brand "Toyota") (define car-3-color black) ;; Flytta car-1 ett steg till höger (set! car-1-x-pos (+ car-1-x-pos 1)) 3

;; Flytta car-3 ett steg till vänster (set! car-3-x-pos (- car-3-x-pos 1))... Detta är ett ganska litet exempel, men redan vid 10 bilar har vi nu 60 variabler att hålla reda på. Att ha alla dessa variabler globala innebär stora risker för misstag och bidrar till en väldigt ostrukturerad kod. Det är här rätt lätt att falla för frestelsen att bara slänga upp ett antal variabler globalt och sedan ändra dem med hjälp av set! när vi exempelvis vill flytta bilen ett steg till höger. Detta resulterar dock snabbt i stora mängder kod som både är oflexibel och jobbig att felsöka. Vi har också väldigt svårt att skapa en procedur som kan ändra på positionen av en viss bil. Vill vi uppdatera alla tio bilarna, måste vi göra tio kodrader där vi inkluderar bilarnas namn 1. 2.2 Ett steg på vägen, muterbara objekt En lätt förbättrad variant vore att vi skriver ett sätt att skapa och hantera bilar en i taget, istället för att varje bil är en uppsättning lösa variabler. (define (make-car x y vx vy brand color) (mlist x y vx vy brand color)) (define (x-pos car-obj) (mcar car-obj)) (define (change-x-pos! car-obj new-x) (set-mcar! car-obj new-x)) ;;... fler funktioner... (define car1 (make-car 12 3 2 0 "VOLVO" white)) (define car2 (make-car 37 3 3 7 "SAAB" red)) (define car3 (make-car 99 24 0 0 "Toyota" black)) (change-x-pos! car1 (+ (x-pos car1) 1)) ;; Flytta bil 1 ett steg till höger (change-x-pos! car3 (- (x-pos car3) 1)) ;; Flytta bil 3 ett steg till vänster Vi har nu fångat en viktig del: all data som kan ändras hos en bil finns samlad på ett enda ställe. All information som rör bil 1 finns i car1, all information som rör bil 2 finns i car2 och så vidare. Vi kan alltså till exempel skapa en procedur som tar in en bil, och ändrar på just den bilen (utan att veta vad den heter). (define (move-car! car-obj steps) 1 Om man inte använder makron, vilket ligger utanför denna kurs. Att lösa just detta problem med makron är dock att börja i helt fel ände. 4

(change-x-pos! car-obj (+ (x-pos car-obj) steps))) (move-car! car1 1) Det vi har gjort hittills liknar ADT-beskrivningen i laboration 2 väldigt mycket. Vi buntar ihop information, och har ett sätt att ta ut den som inte är beroende av att man vet exakt hur den lagras (man skriver x-pos, och struntar i om positionen lagras i en lista, sträng, vektor,... ). Skillnaden hittills är att man tänker sig att varje bil har ett tillstånd som kan ändras. Vi kan gå in och ändra i en viss bils position, snarare än att skapa en helt ny bil som har den nya hastigheten. Samtidigt har vi inte samlat precis all information som rör en viss bil. För att hämta ut information om en bils x-position behöver vi proceduren x-pos. För att ändra på en bils x-position behöver vi change-x-pos!. Och så vidare. Nu hittar vi ett sätt att bunta ihop all information om ett objekt 2.3 Att skriva mallar Vi bygger nu upp en mall som gör att allt som berör en viss bil, hamnar i bilen själv. Vi gömmer alltså både undan hur informationen lagras (som vi gjorde här ovan), och gör alla procedurer som behövs för att hämta och modifiera information till något som finns i ett bilobjekt. Målet är att kunna skriva saker som (send car1 move 1), och låta all hantering som gör att bilen flyttas finnas i själva car1. Därför skriver vi en mall, som innehåller all den information som varje bil ska innehålla. Denna mall kallas för klass och är gemensam för alla likadana objekt. För bilexemplet ovan kan vi skapa en klass car% som fungerar som mall för samtliga bilar. I klassen definierar vi vilka egenskaper som objekten ska ha (en bil ska ha en x-position), medan själva värdena hos bestäms när de enskilda objekten (bilarna) skapas (car1 ska ha x-position 12 till att börja med, car2 ska ha x-position 37 och så vidare). 2.3.1 Med objektorientering Genom att lägga ned lite mer arbete initialt med att skapa en klass får vi kod som blir kortare, är mycket lättare att utöka och dessutom enklare att felsöka. I längden blir det med andra ord betydligt mindre arbete. 5

Så här ser vår första utkast till mall ut (vi kommer att bygga ut exemplet senare): (define car% (init-field x-pos ;; lokala variabler y-pos x-velocity y-velocity brand color) (define/public (move distance) (set! x-pos (+ x-pos distance))) (define/public (get-position) (cons x-pos y-pos)) (super-new))) (define car1 (new car% [x-pos 12] [y-pos 3] [x-velocity 2] [y-velocity 0] [brand "VOLVO"] [color black])) ;; det finns alternativa sätt att använda mallen för att skapa bil-objekt (define car2 (make-object car% 37 3 3 7 "SAAB" red)) (define car3 (make-object car% 99 24 0 0 "Toyota" black)) ;; Flytta car-2 ett steg till höger (send car2 move 1) ;; Skriv ut positionen för car-3 före move (send car3 get-position) ; -> (99. 24) ;; Flytta car-3 tre steg till vänster (send car3 move -3) ;; Skriv ut positionen för car-3 efter move (send car3 get-position) ; -> (96. 24) 2.4 Lite filosofi - var sak på sin plats Här innan har vi använt objektorientering som något slags synonym till att bunta ihop information. Det finns såklart mycket mer att säga än så (och ni kommer att gå in djupare på detta i senare kurser). Än så länge räcker det att säga att objektorientering handlar om att programmera med objekt som 6

är i ett visst tillstånd 2. Varje bil har kunskap om sin position, hastighet och så vidare. Flyttar vi på en bil, är det den bilen som uppdaterar sin lokala information om var i världen den befinner sig. är isolerade från varandra. När vi bygger objektorienterade program, ger vi de olika objekten ansvar att hålla reda på sin egen information. En bil vet om sin, och ingen annans, position. Och när vi designar programmet skapar inte (till exempel) något slags stor tabell som innehåller alla bilars positioner. Detta är inte nödvändigtvis bättre eller sämre, utan det sätt som man använder när man objektorienterar. vi kan kommunicera med på ett bestämt vis, med bestämda följder. Det andra delar av programmet behöver veta om en bil, är att man kan fråga den move 1 om man vill att den ska flytta 1 steg, och att om man frågar den get-position så kommer den att svara med data på formatet (x-position. y-position). Hur bilen fungerar internt - hur positioner lagras till exempel - är dess ensak. kan bete sig på egna vis. Vi vet att en bil kommer att flytta sig när man ber den flytta sig. Men vi skulle kunna skapa två olika slags bilar, som hanterar kommandot flytta dig på olika vis. I ADT-fallet med muterbara strukturer som vi hade ovan, betér sig alla bilar likadant. Det kravet släpper vi på här. I senare avsnitt kommer vi att diskutera flera andra viktiga koncept (som arv, vilket ni inte behöver kunna i laboration 4), så se detta som det absolut minsta ni behöver veta för kursen. 3 Klasser Nu när vi har etablerat vad ett objekt är kan vi börja titta på de mallar efter vilka objekten konstrueras, nämligen klasser. En klass kan ses som ett kokboksrecept från vilken objekt av samma typ skapas. I Racket är konventionen att skriva klassnamnen med ett procenttecken (%) som sista tecken för att förtydliga att det rör sig om en klass. Dessutom finns generellt för alla språk konvetionen att namnge klasser med substantiv i singularform. Exempel på klassnamn kan vara rymdskepp%, spelare%, sorterare%, kokbok%, etc. 2 Man säger att de har tillstånd /state 7

3.1 Exemplet car% För att exemplifiera de olika delarna av en klass använder vi en något utökad version av bilexemplet. Nedan kommer vi att gå igenom de olika delarna av exemplet, och förklara dem närmare. (define car% (init-field x-pos y-pos x-velocity y-velocity brand [color black]) (field [age 0]) ;; Tests if the car is old. (define/public (old?) (> age 50)) ;; Calculates the x-position after moving. (define/private (after-move steps) (+ x-pos (* x-velocity steps))) ;; Checks if it is legal to move n steps. (define/private (legal-move? n) (>= (after-move n) 0)) ;; Executes a move, if legal. (define/public (move steps) (set! age (+ age 1)) (cond [(old?) (error "Car is old and busted!")] [(legal-move? steps) (set! x-pos (after-move steps))] [else (error "Outside!")])) ;; get the x position of the car (define/public (get-x) x-pos) ;; --- Förenklad hantering. Enbart flytt i x-led --- (super-new))) 8

3.2 Fält Fält lagrar de värden som ett objekt håller reda på. Här är de intressanta delarna av koden: (init-field x-pos y-pos x-velocity y-velocity brand [color black]) (field [age 0])...) De variabler eller egenskaper som är kopplade till ett objekt kallas fält. Vilka värden fälten har kommer att variera mellan olika objekt. I vårt exempel har varje bil sin egen x-position, y-position,... och en ålder (age). I Racket finns två olika typer av fält som vi kan använda: field och init-field. Skillnaden mellan dessa är att init-field är initierbara. Detta är alltså värden som vi kan skicka med när vi skapar ett nytt objekt (information som att car1 har x-position 12 från början). 3 Vanliga field har däremot ett fördefinierat värde som (från början) är detsamma för alla instanser av en klass. Varje bil börjar i vårt exempel med åldern 0. Värdena kan däremot nås/ändras under tidens gång. När vi ändrar värdet på ett fält för ett visst objekt, betyder det inte att alla objekt av den aktuella klassen ändras. Vi kan till exempel uppdatera hastighet och ålder för car1 utan att car2 påverkas, även om båda är skapade utifrån samma mall. I exemplet har vi också en parameter som fått klamrar runt sig, och ett värde. Detta innebär att den har ett defaultvärde. Om man inte anger vilken färg en bil ska ha (när man skapar den), blir den automatiskt svart. Alla de andra parametrarna måste man ange värden för, när man skapar bilen. 3 Formellt säger vi att dessa fält får sina värden från inparametrarna vid initiering av en klassinstans. 9

3.3 Metoder Ett objekts metoder är dess lokala procedurer. ;;... (define/public (old?) (> age 50)) (define/private (after-move steps)... ) (define/private (legal-move? n)... ) (define/public (move steps)... ) (define/public (get-x) x-pos)...)) En procedur som tillhör ett specifikt objekt brukar kallas för en metod. Dessa definieras på ungefär samma sätt som vanliga procedurer, och placeras inuti själva klassdefinitionen. Detta gör att varje objekt sedan kommer att ha tillgång till dem. Varje bil kommer exempelvis att ha tillgång till legal-move? eller old?. Vi kan applicera/köra vissa procedurer genom send: > (send car1 get-x) 12 > (send car1 move 10) ;; argumentet kommer direkt efter > (send car1 get-x) 22 3.3.1 public och private Vissa metoder vill man göra tillgängliga utåt, och låta andra delar av programmet nå. I vårt bilexempel har vi en metod move för att flytta vår bil i x-led på ett kontrollerat sätt, som definitivt bör vara tillgänglig. Detta syns på att vi skriver define/public. Vi har också hjälpfunktioner som egentligen bara är användbara internt när en bil beräknar var den ska flytta sig (om den ska flytta sig) - predikatet legal-move? och funktionen after-move. Dessa behöver inte synas utåt, och anges som privata. Detta innebär (ungefär) att andra delar av programmet inte kan använda dem. Alla publika metoder kan anropas utifrån med hjälp av send som beskrivs i avsnitt??. Om man inte anger någon åtkomsttyp (och skriver define) så blir metoden ändå privat men det tillhör god sed och förbättrar läsbarheten att förtydliga detta genom define/private. 10

Det finns ett flertal andra åtkomsttyper som kan användas (se http://docs.racket-lang.org/ reference/createclass.html) men privata och publika metoder är de absolut vanligaste formerna att använda. I laboration 4 kommer du inte behöva fundera över några andra. 3.4 Selektorer och Mutatorer ;;... (define/public (move steps) (set! age (+ age 1)) (cond [(old?) (error "Car is old and busted!")] [(legal-move? steps) (set! x-pos (after-move steps))] [else (error "Outside!")])) (define/public (get-x) x-pos)...)) En selektor är en metod som returnerar värdet av ett fält, medan en mutator är en metod som ändrar värdet av ett fält. Dessa metoder är oftast väldigt simpelt uppbyggda och returnerar/ändrar bara värdet på ett fält utan ytterligare beräkningar. I vissa fall kan man vilja utföra en del beräkningar i dessa också, men det är ofta inte nödvändigt. För att tydliggöra att en metod är en selektor eller mutator brukar dessa metoder namnges med get- respektive set- följt av fältets nman. För att till exempel returnera och änrdra en variabel som innehåller ett objekts hastighet kan vi därmed skapa selektorn get-velocity och mutatorn set-velocity. I vårt bilexempel är get-x en typisk selektor. Rena mutatorer kan dock vara farliga då de lätt kan resultera i underliga beteenden (exempelvis att en bil teleporteras från en plats till en annan). Därför har vi istället för en ren set-x! skrivit en metod move som hanterar det på ett mer kontrollerat sätt. Den ser till så att vår bil inte är för gammal att använda, att bilen inte hamnar utanför vårt koordinatsystem, och garanterar att att all förflyttning sker relativt den nuvarande positionen. 4 3.5 this Varje objekt kommer att ha tillgång till något som representerar sig självt. Detta kan tyckas lite onaturligt till att börja med, men är rätt praktiskt. Om två objekt ska känna till varandra på något vis (i laboration 4 behöver ni få rum att känna till vilka figurer som finns där, och få figurerna att känna till vilket rum de står i, exempelvis), är det extremt användbart. I en klassdefinition 4 Det finns särskilda Racketprocedurer för att ändra värden på fält utifrån. Dessa bör överlag undvikas. 11

kallas denna hänvisning till det egna objektet för this. Vi har ingen naturlig användning av det i exemplet ovan, utan skapar ett trivialt exempel: (define trivial% (init-field name) (define/public (get-name) name) (define/public (get-object) this) ;; returnerar själva objektet (super-new))) Användningsexempel: > (define apa (new trivial% [name "Apan"])) > (send apa get-name) "Apan" > (send apa get-object) (object:trivial%...) > (send (send apa get-object) get-name) "Apan" > (eq? apa (send apa get-object)) ;; är det exakt samma objekt? #t 4 Att skapa instanser av en klass Instansiering handlar om att skapa objekt av en viss klass. I vårt exempel handlar detta om steget där vi använder bil-mallen för att skapa de enskilda bilarna (car1, car2,... ). Objekt kan instansieras på lite olika sätt. Ett sätt är att använda new på formen nedan: (new klassnamn% [parameternamn1 värde1] [parameternamn2 värde2] [parameternamn3 värde3]) Den största fördelen är att det blir väldigt tydligt vad som är vad och vilka parametrar som används. Dessutom spelar ordningen man anger parametrarna i ingen roll. De fält som har defaultvärden/standardvärden kan helt enkelt utelämnas. Nackdelen är att det kan bli ganska mycket kod, framför allt om man ska skapa många instanser av samma klass. Vissa klasser har även vissa parametrar som måste anges, men det ges det bra felmeddelanden om. 12

Ett annat sätt att instansiera en klass är med hjälp av make-object. Där anger man bara värdet på parametrarna. Detta gör att ordningen börjar spela roll. Vilken ordning parametrarna ska komma i för inbyggda klasser (och för den delen vilka möjliga parametrar som finns) anges i dokumentationen (http://docs.racket-lang.org/). För de klasser vi själva skapat anges parametrarna i samma ordning som i klassdefinitionen (init-field-delen). Detta blir kompaktare i koden, men samtidigt mycket otydligare. (make-object klassnamn% värde1 värde2 värde3) Man kan även använda instantiate som är en kombination av new och make-object. Vissa klasser har även egna konstruktorer som har andra egenskaper än de övriga. Exempelvis finns det en make-bitmap som skapar en instans av klassen bitmap%. 5 Kommunicera med objekt Kommunikationen med objekt sker i Racket genom kommandot send på formen (send objekt kommando <parameter1> <parameter2>...) Detta kan användas för att anropa de olika metoder som är kopplade till objekten. Några exempel på hur sådana kommandon kan se ut är: (send rymdskepp get-velocity) (send user set-name "James Bond") (send calculator multiply 2 3 8) 6 Arv (ingår 2016!) 6.1 Motivering I vårt bilexempel använder vi genomgående en typ av bil. I ett bilspel skulle vi kanske vilja ha flera olika typer av bilar, alla med sina egenheter (Bondbilar skjuter raketer, Trabis går sönder lätt). Däremot ska de alla kunna göra det bilar gör, och programmet ska kunna kommunicera med dem på det sätt som väntas av bilar. Istället för att ägna oss åt att kopiera en större mängd kod (vilket, utöver att vara fult, dessutom tar bort all form av logisk struktur), vill vi alltså uttrycka något slags koppling mellan car% och bond-car%. Detta gör vi genom att låta egenskaper gå i arv. 13

Vi definierar först en car%-klass, och beskriver sedan vad som skiljer en bond-car% från en car%. Vi säger att car% är föräldraklass till bond-car%, och att bond-car% ärver egenskaper från den. Andra uttryck är superklass / subklass eller basklass / subklass. Vid konstruktion av större program används ofta ganska stora arvshirarkier med ett flertal nivåer. Det kan också förekomma mer komplicerade arvs-strukturer. Om en bond-bil% ska kunna åka under vatten, kanske den ärver egenskaper från såväl car% som boat%. Detta för med sig en del intressanta detaljproblem. I vår beskrivning begränsar vi oss till enkla arv (där varje klass som mest har en förälder). 6.2 Exempel: utökad klass Vi vill definiera ett slags bil som har alla egenskaper som en vanlig bil har, och dessutom två extra: (define bond-car% (class car% (inherit move) (define/public (shoot) pew-pew) (define/public (boosted-move) (move 1000)) (super-new [brand aston-martin]))) Användning visar hur det fungerar: > (define cdr007 (new bond-car% [x-pos 1] [y-pos 1] [x-velocity 100] [y-velocity 200])) > (send cdr007 boosted-move) ;; extrametoden vi definierade > (send cdr007 get-x) 100001 > (send cdr007 move 15) ;; visar att vanliga car%-metoder fungerar > (send cdr007 get-x) 101501 > (send cdr007 shoot) pew-pew Notera att vi var tvungna att skriva (inherit move) för att kunna använda det direkt 5. 6.3 Överlagring En subklass ärver normalt alla de metoder som föräldraklassen innehåller. Ofta vill vi dock omdefiniera metoden för att uppfylla en mer specifik funktion i subklassen. Detta görs genom att skriva 5 Vi skulle tekniskt sett kunna ersätta det med (send this move 1000), vilket passas vidare till car%-metoden. Det är dock fult. 14

(define/override... följt av den nya definitionen. Exempel: (define trabi% (class car% (inherit-field age) ;; vi vill kunna ändra på en bils ålder (define/override (move steps) (when (> steps 2) (set! age 99999)) ;; om man tar många steg, dör bilen (super move steps)) ;; vi kan här kalla på föräldraklassens move (super-new [brand trabant]))) En testkörning: > (define wf1989 (new trabi% [x-pos 0] [y-pos 0] [x-velocity 0] [y-velocity 0])) > (send wf1989 move 1) > (send wf1989 move 10).. Car is old and busted! En överlagrad metod får samma åtkomsttyp som den ursprungliga metoden om inte annat anges genom att man lägger till /public eller /private. Detta kan man naturligtvis lägga till även om åtkomsten ska vara densamma för att öka läsbarheten. Det finns mycket mer att säga om överlagring och arv. Läs mer i Racket Guide [? ]. 6.4 Arv i flera nivåer Det finns inga hinder mot att skapa flera nivåer av arv: (define powertrabi% (class trabi% ;; vi ärver alla egenskaper en trabi% har (define/public (shoot) pow-pow) (super-new [x-velocity 100] [y-velocity 100] [color black]))) > (define pt1989 (new powertrabi% [x-pos 0] [y-pos 0])) > (send pt1989 shoot) ;; definierad i powertrabi% pow-pow > (send pt1989 move 1) ;; använder trabi%-move > (send pt1989 get-x) ;; get-x definierad i car% 100 > (send pt1989 move 3) ;; vi använder faktiskt trabi%:s move, och inte car%:s.. Car is old and busted! 15

7 Exempel på en enkel klass Nedan följer ytterligare ett exempel på hur vi kan bygga upp en klass i Racket. Ordningen är inte helt viktig. Klassen i fråga har inga praktiska användningsområden, men den illustrerar de olika delar som tagits upp i de tidigare avsnitten. Det första vi måste göra är att ange vilken klass som vår klass ska ärva ifrån. Samtliga klasser ärver egenskaper från object%, eller från någon (direkt eller indirekt) subklass till object%. Kodsegmentet nedan talar om att vi vill skapa klassen my-class% som ärver från object%. (define my-class%... )) Därefter deklarerar vi klassens fält. De fält som anges som field har som sagt fördefinierade värden, medan de som anges som init-field anges vid instansiering av klassen. Fundera på vad som ska vara unikt (från början) för ett objekt, och skriv sådana fält som init-field. Standardvärden anges inom klammerparenteser. Om vi inte anger något värde på fältet vid instansiering, kommer det att få detta värde istället. (define my-class% (init-field obligatory1 obligatory2 [optional1 3.14] [optional2 "Pi"]) (field [stat1 "TDDC"] [stat2 74])... Nästa steg är att definiera de metoder klassen ska ha. Om du vill hämta ut information från ett 16

objekt, skriv selektorer för detta (som get-stat nedan). (define my-class% (init-field obligatory1 obligatory2 [optional1 3.14] [optional2 "Pi"]) (field [stat1 "TDDC"] [stat2 74]) (define/private (add-two n) (+ n 2)) (define/public (get-stat) (cons stat1 stat2)) (define/public (next-course) (set! stat2 (add-two stat2)))... Nu återstår bara en sista sak innan vi har en fungerande klass. Vi måste lägga till ett anrop till super-new. Denna rad kan läggas vart som helst i klassdefinitionen, men läggs lämpligen först eller sist. Detta gör det möjligt att instansiera klassen med hjälp av new och make-object. (define my-class% (init-field obligatory1 obligatory2 [optional1 3.14] [optional2 "Pi"]) (field [stat1 "TDDC"] [stat2 74]) (define/private (add-two n) (+ n 2)) (define/public (get-stat) (cons stat1 stat2)) (define/public (next-course) (set! stat2 (add-two stat2))) (super-new))) Nu har vi en helt fungerande klass, även om just detta exempel inte kan användas till något vettigt. Såhär skulle man exempelvis kunna skapa två instanser av klassen: (define instans1 (make-object my-class% 1 2 3.14159)) (define instans2 (new my-class% [obligatory1 1] [obligatory2 2] [optional2 "Pie"])) Komandon skickas till objekten på precis samma sätt som för de inbyggda klasserna. Med de instanser som skapades ovan kan vi exempelvis göra följande: 17

(send instans2 next-course) (send instans1 get-stat) -> (TDDC. 74) (send instans2 get-stat) -> (TDDC. 76) (send instans1 add-two 7) -> error Observera att om vi försöker ge ett kommando om att använda den privata metoden add-two kommer detta resultera i ett felmeddelade då den inte är känd i den globala omgivningen. Om den önskade metoden tar ett eller flera inargument anges de efter metodnamnet, så som beskrivs i multiply exemplet i avsnitt??. 18