Olio-ominaisuuksien toteuttaminen Schemellä -------------------------------------------- Standardi-Scheme ei varsinaisesti tue olio-ohjelmointia. Tässä tehtävässä tarkoituksena on toteuttaa R5RS:n makroilla [tai modifoimalla SICPin metasirkulaarista tulkkia (käsitellään luennoilla ja kirjassa luvussa 4)] Scheme-kieleen oliolaajennus. Laajennuksen on tuettava perintää (inheritance) ja metodien ylikuormitusta (overloading). Toteuttamasi ominaisuuden ei tarvitse olla täsmälleen alla kerrotun kaltainen. Jos suunnittelet suuria muutoksia aiheeseen, kerro siitä aihevalinnassa. Luokka: ------- Luokkia luodaan class-erikoismuodolla tähän tapaan: (define (class () (private (attribute ) ...) (public (method ) ...) (constructor ))) Attribute-tyyppinen muuttuja voi sisältää minkä tahansa tyyppisen arvon. Method-tyyppiin voi asettaa vain proseduureja eli lambda-lauseita ja niitä ei voi muuttaa. Aliluokissa ei saa käyttää saman nimisiä attribuutteja kuin yliluokissa. Sen sijaan metodien nimet saavat olla samat, tällöin kyseessä on ns. overriding. Metodeihin voi lisätä final määritteen. Tällöin aliluokat eivät saa korvata metodia omilla metodeillaan. Metodi voi olla myös abstrakti (ks. alta). Tällöin luokasta ei voi tehdä ollenkaan instanssia. Tee myös tuki yksinkertaiselle moniperinnälle: jos useampi yliluokista määrittelee samannimisiä metodeja, valitse niistä yksi esimerkiksi siinä järjestyksessä, jossa luokat ovat aliluokan määritelmässä. Private-listan alla oleviin muuttujiin pääsee käsiksi vain olion sisältä. Public-listassa oleviin pääsee käsiksi kaikkialta. Publiciksi määriteltyjä attribuutteihin ei voi viitata suoraan, vaan niitä on kutsuttava erityisten get/set! metodien avulla (ks. Attribuuttien käsittely.) Constructor-listalla määritellään luokan konstruktori, jota kutsutaan instantioitaiessa luokasta oliota. Konstruktoria ei ole pakko määritellä. Konstruktori-listan alkiot evaluoidaan järjestyksessä samalla lailla kuin begin-erikoismuodonkin. Luokan määrittelystä voi jättää pois private- tai constructor- listan. Luokassa täytyy olla määritelty ainakin yksi julkinen metodi. Mikäli näin ei ole, tulee tästä antaa virheilmoitus. Luokan instantiointi tapahtuu kutsumalla make-instance erikoismuotoa. (Ks. luokan instantiointi) Tee lisäksi tuki luokkamuuttujille (muuttuja, jonka arvo on tallessa luokassa eikä yksittäisissä olioissa) sekä tapa kutsua yliluokan samannimistä metodia ("super"). Attribuuttien käsittely ----------------------- Jos attribuutti on määritelty näkyvyydeltään privateksi, voi siihen viitata vain sisäpuolelta. Jos attribuutti on määritelty publiciksi, generoidaan luokalle attribuuttia käsittelevät get-/set- metodit. Syntaksi on seuraava > (object 'get-) value > (object 'set- value) get-metodi ei ota argumentteja ja palauttaa attribuutin arvon. set!-metodi ottaa argumentikseen asetetttavan arvon ja paluuarvoa ei ole määritelty. Luokkaan voi määritellä ekspilisiittisesti omat get-/set-, joiden on toteuttava edellä esitetyt vaatimukset. Mikäli omat metodit määritellään käytetään niitä, eikä implisiittisesti määriteltyjä. Metodien käsittely ------------------ Jokaiseen metodiin lisätään implisiittinen this-argumentti, joka viittaa make-instancella luotuun olioon. Oliolaajennuksen on tuettava ns. dynamich dispatchia eli sitä, että metodeista kutsutaan aina sitä, joka on määritelty alimassa luokassa tai sitä perintähierarkiassa lähinnä olevassa yläluokassa. (Katso malliksi kotitehtävää 3.3.) Mikäli metodi ei löydy luokasta, etsitään sitä yläluokista lähtien ensimmäisestä () listan osoittamasta yläluokasta. Siis jos metodi on määritelty monessa yläluokassa, valitaan aina järjestyksessä ensimmäinen luokka, joka sisältää metodin. Metodien määrittelyn syntaksi: (method ( ) Mikäli määritellään ei-overloadattava metodi: (method final ( *) ) Abstraktit metodit määritellän näin: (method ( *)) Siis metodilla ei ole runkoa ollenkaan. Instantiointi: -------------- Instantiointi tapahtuu kutsumalla make-instance erikoismuotoa. Syntaksi on seuraavanlainen: (define (make-instance *)) Make-instance instantioi luokan. Mikäli luokassa on määritelty konstruktori, annetaan sille make-instancessa määritellyt argumentit ja konstruktori suoritetaan instantioidussa objektissa. Esimerkki luokan määrittelystä: (define polygon (class () (private (attribute vertices '())) (public (method (area) ;; Laskee pinta-alan geneerisesti monikulmiolle. ..) (method (whoami) 'polygon) (method (print-vertices!) ; do fancy printing )) (constructor (lambda (v) (set! v vertices))))) (define rectangle (class (polygon) (public (method (area) ;; Laskee pinta-alan suorakulmiolle. (method (whoami) 'rectangle)) (constructor (lambda (first second) (set-attribute! vertices (list first second)))))) Esimerkki instantioinnista: > (define square (make-instance rectangle '(10 . 0) '(0 . 10))) > (square 'area) 100 > (square 'whoami) rectangle > (square 'print-vertices!) first (x,y): 10 0 second (x,y): 0 10