Föreläsningar okt 29 v 44. OO på allvar.

Grovplan för föreläsningar och hemuppgifter v 44 till v 48

Vecka Teori Föreläsning Redovisning hemuppgifter

katalog -> CBA B A

v

v44 Objektorientering

Komplexa tal OO

DD chapter 8

Tisdag i F1

Kontrollskrivning

v45 Arv DD chapter 9 OO Vector OO Rational Typ av drag

Interface Tillämpning: (Bråk) på bräde

Gadget (31 rader) 102 rader 140 rader

MoveGadget (21 r)

MoveGadget TestRational TestPath

TestVector

v46 GUI inherit Vector med arv (90r) Fyrhörningar Schackpjäser

Lyssnare, 15-spelet Gadget on Board arv arv

DD chapter 12, 13 (50 rader) (+ 200 rader)

MoveGadget DrawQuadr.. TestChessmans

v47 Klockor, trådar gui Enkel GUI Luffarschack Schackbräde

15-spelet, Undantag som syns som syns som syns

DD ch 15 (100 rader) (84 rader) (+ 100 rader)

PlayGame RitaLSchack PlayChess

v48 Inl datastrukturer game spelbart Spelbart Spelbart Schack

Algoritmer Vick-spel luffarschack

(Nybörjarplattan (+ 70 rader) (+90 rader)

Labyrintspel)

(~+75rader)

PlayGame SpelaLSchack PlayChess

Klassfiler för en del av mina preliminära lösningar finns på info/inda. t ex ett körbart system för spelbart luffarschack på info/inda/game/B, som körs som (namnet på klassen med main finns i tabellen ovan) .....>java Test LSchack

Klassfiler för schackpjäser på info/inda/inherit/A osv. Lösningarna är ibland ganska primitiva (t ex avbryts en del testprogram med styr-c, information om felaktiga drag i terminalfönstret istället för i bilden osv). Dina lösningar får gärna vara mer sofistikerade.

Komplexa tal definierade som ADT-objekt med alla

operationer som instansmetoder.

Vi ska nu flytta alla klassmetoder i Comp till Complex och göra dem till instansmetoder.

Vår egendefinerade typ Complex för komplexa tal i klassen Complex har som förut privata instansvariabler , en instansvariabel för det komplexa talets realdel och en instansvariabel för det komplexa talets realdel. Liksom förut måste vi kunna skapa nya komplexa tal, vilket vi som förut gör med konstruktorn public Complex(double ire, double iim), och kunna få tillgång till ett komplext tals realdel och imaginärdel, vilket vi som förut gör med acessmetoderna public double re() och public double im().

Men nu flyttar vi alla klassmetoderna i Comp in i Complex och gör om dem till instansmetoder, dvs det står inte static i metoddefinitionen. Märk också att t ex add som tidigare hade två parametrar av typ Vector nu har bara en, dvs ett komplext tal "kan själv" addera ett annat komplext tal till sitt eget värde och returnera resultatet, ett nytt komplext tal. Metoder som tidigare hade en parametrar av typ Vector saknar nu parametrar, se t ex conjugate(), modulus() och toString() :

public class Complex {

private double re;

private double im;

public Complex(double ire, double iim) {

re = ire;

im = iim;

}

public double re(){

return re;

}

public double im(){

return im;

}

public boolean equals(Complex z2){

return (re == z2.re) && (im == z2.im);

}

public Complex add(Complex z2){

return new Complex( re + z2.re, im + z2.im) ;

}

public Complex sub(Complex z2){

return new Complex(re - z2.re, im - z2.im);

}

public Complex mult(Complex z2){

return new Complex(re*z2.re - im*z2.im,

re*z2.im + im*z2.re );

}

public Complex conjugate(){

return new Complex( re , - im );

}

public double modulus(){

return Math.sqrt(re*re + im*im);

}

public Complex div(Complex z2){

if (z2.equals(new Complex(0,0))) {

throw new RuntimeException("Tried to divide by 0 + 0i");

} else {

double mo = z2.modulus();

double dd = mo*mo;

Complex zz = mult(z2.conjugate());

return new Complex( zz.re/dd, zz.im/dd);

}

}

public String toString() {

return re + " + " + im + "i";

}

}

Om vi i en metod gör detta: eller (samma sak):

Complex result; Complex result = new Complex(3.0, 4.0);

result = new Complex(3.0, 4.0);

händer detta:

Vi kan nu göra beräkningar på komplexa tal med TestComplex , men detta program ser nu annorlunda ut, instansmetoderna anropas med komplexa-tal-objekt före punkten:

public class TestComplex {

public static void main(String [] iargs) {

Complex z1 = new Complex(3, 4);

Complex z2 = new Complex(1, -2);

Complex z3 = new Complex(3, 2);

Complex z4 = new Complex(4, 5);

Complex z5 = new Complex(2, 1);

Complex z6 = new Complex(0, 5);

System.out.println(" (3+4i)= " + z1);

System.out.println(" (3+4i)/(1-2i) = " + z1.div(z2));

System.out.println(" (3+2i)(4+5i) = " + z3.mult(z4));

System.out.println(" Ex 10.2.14 =

" + (z2.div(z1)).sub((z5).div(z6)));

}

}

TestComplex, kan även skrivas utan att man namnger de komplexa talen:

public class TestComplex {

public static void main(String [] iargs) {

System.out.println(" (3+4i)= " +

new Complex(3, 4));

System.out.println(" (3+4i)/(1-2i) = " +

(new Complex(3, 4)).div(new Complex(1, -2)));

System.out.println(" (3+2i)(4+5i) = " +

(new Complex(3, 2)).mult(new Complex(4, 5)));

System.out.println(" Ex 10.2.14 = " +

((new Complex(1, -2)).div(new Complex(3, 4)).sub(

(new Complex(2, 1)).div(new Complex(0, 5)))));

}

}

Som synes hamnar nu t ex add mellan de två komplexa tal som skall adderas, dvs "infix", vilket påminner om bruket att använda + i matematiken. Vad som däremot känns mer främmande är att det komplexa tal som står till vänster är de tal som "gör jobbet" och "rår om metoden", symboliserat med punkten.

Funktionellt och imperativt. Två olika sätt att programmera (två "programmeringsparadigmer") :

Märk också att ingen av metoderna i Complex modifierar ett komplext tal, instansvariablerna ändrar aldrig de värden de fått när det komplexa talet skapades med new + konstruktorn. Sådana metoder motsvarar vad som i matematiken kallas funktioner. Beräkningar görs genom att man skapar nya komplexa tal.

Detta sätt att programmera kallas funktionell stil och är det sätt man uteslutande använder i funktionella språk . Funktionell stil påminner mycket om matematik, beräkningarna uttrycks med uttryck och tilldelning spelar ingen eller liten roll. Funktionell stil är även i ett språk som java lämpligt för matematiska objekt såsom komplexa tal, matriser, vektorer i planet och rymden osv.

I funktionell stil programmerar man genom att skriva uttryck som använder funktioner som man definierar. Programmering i riktiga funktionella språk leder till korta och eleganta program som också är relativ lätta att skriva korrekt och är ett naturligt sätt att programmera för den som kan matematik.

Vad händer med alla mellanvärden som beräknas och ger nya objekt som efter ett tag aldrig mera används? I Java "städas" icke-längre-refererade objekt bort av en skräpsamlare, "garbage collector". I språk som saknar automatisk skräpsamlare är det mer problematiskt att programmera med funktionell stil.

I imperativ stil har man också metoder som modifierar instanvariabler, dvs objekten ändras när programmet körs. Dessa metoder bör normalt ha returtypen void och därmed anropas som kommandon (satser). I denna stil "händer det saker" med objekten. Programmeringen innebär ofta att datorprogrammet är en modell av en del av verkligheten, och körningen är en simulering av vad som händer i verkligheten. I imperativ stil använder man både uttryck och satser (t ex tilldelningar), och definierar både void-metoder och returnerande metoder. För att förstå programmet måste man tänka sig in i vad som händer under körningen och objektens tillstånd. Det är till stor del detta som (åtminstone enligt förespråkare för funktionell stil) som gör imperativ programmering så svårt. I ett bra imperativt program är så få tillstånd samtidigt relevanta för förståelsen av programmet. Man ska till exempel använda lokala variabler så mycket som möjligt, eftersom dessa variablers värde saknar betydelse för andra metoder,

använda privata instansvariabler vars värde bara kan ha betydelse för objektet själv osv.

Trots att funktionell programmering på många sätt är ett bra programmeringssätt, är imperativ programmering det vanligaste sättet att programmera, delvis på grund av traditionens makt. Det vanliga sättet att använda objektorienterad teknik är en utvecklad och förfinad form av imperativ programmering. I fortsättningen i kursen kommer vi att se många exempel på objekt som modftieras under körningen. Ett första exempel var objekten av typen (klassen) Time (förra föreläsningen) som hade en metod setTime för att ändra tiden.

I fortsättningen i dennna kurs kommer vi för de mesta skriva klasser som ska fungera som nya typer, dvs i användande program skapar vi objekt. Dessa objekt kommer att ha egna instansvariabler och instansmetoder, dvs vi kommer sällan att skriva klassmetoder (static) och klassvariabler (static) .

Klassvariabler och klassmetoder i klasser som främst är skrivna för att kunna skapa objekt.

Tidigare i dennna kurs när vi använt Java som ett icke-OO-språk har vi skrivit klasser som haft som uppgift att innehålla enbart klassmetoder och eventuelt även klassvariabler.

Men även i klasser som ska fungera som nya typer , dvs klasser som i användande program används för att skapa nya objekt, kan de finnas enstaka klassvariabler och klassmetoder. Dess variabler blir då gemensamma för alla objekt och finns i ett enda exemplar oberoende av om hur många objekt som skapas (t o m om inte ett enda objekt av klassen skapas). Detta framgår också av minnesbilderna:

Variabeln num skulle t ex kunna användas för att hålla reda på hur många objekt vi skapat, och räknas upp med +1 varje gång ett nytt objekt skapas men anrop av konstruktorn /konstruktorerna.

Vissa Java-programmerare lägger ett static void main i klasser för kunna testköra klassen men denna main-metod används inte när klassen används på allvar. Jag brukar dock lägga main i en egen klass.

Ett exempel på användning av klassvariabler är att skapa motsvarigheten till uppräkningstyper. I en av hemuppgifterna skall vi skapa en klass för typen Path, drag på ett schackbräde. Vi vill kunna fråga ett Path-objekt om det är ett rakt drag(som torn i schack får gå), ett diagonalt drag (som löpare får gå) , ett "hästdrag", ett "drag" till samma ruta eller ett annat sorts drag. Detta brukar i Java göras med en metod som returnerar ett värde av typen int :

public int getKind()

//returnerar STRAIGHT / DIAGONAL / HORSE / NOMOVE / OTHERMOVE

För att göra programmet mer lättläst brukar man döpa de olika heltal man vill sända tillbaka till lämpliga namn, som man brukar göra till static final :

public static final int STRAIGHT = 0;

public static final int DIAGONAL = 1;

public static final int HORSE = 2;

public static final int NOMOVE = 3;

public static final int OTHERMOVE = 4;

Det reserverade ordet final innebär att variabelvärdet inte får ändras, namnet på variabeln är namnet på en konstant. Många programmerare ger konstanter namn med enbart versaler. Eftersom det är "onödigt" att varje objekt har egen uppsättning av konstanterna kan man lämpligen göra dem till klassvaribler med static.

Om instansvariabler som är objekt och om this .

En klass för att beskriva personer. Det vi är intresserad av är personers namn, om dom är gifta

och i så fall med vem.

Vi studerar följande program:

public class HanOchHon {

public static void main(String [] arg) {

Person per = new Person("Per");

Person karin = new Person("Karin");

per.friarTill(karin); // ->

System.out.println(per + "\n" + karin);

}

}

class Person {

private boolean gift = false;

private Person makeMaka;

private String namn;

public Person(String inamn) {

namn = inamn;

}

public void friarTill(Person p) {

if (p.villDuGiftaDigMed(this)) {// per anropar karins villDu..->

makeMaka = p;

gift = true;

}

}

public boolean villDuGiftaDigMed(Person p) {// karin kör sin villDu..

if (!(gift)) {

// p.getAge(); p.getLooks(); p.getIncome(); ...

if (Math.random() <0.99) { //Svarar ja

makeMaka = p;

gift = true; //Bilden nedan visar situationen i detta ögonblick

return true;

} else {

return false;

}

} else {

return false;

}

}

public String toString() {

return ("Namn : " + namn + " " +

(gift?("Status : gift med " + makeMaka.getName()) :""));

}

public String getName() {

return namn;

}

}

/* Körresultat :

Namn : Per Status : gift med Karin

Namn : Karin Status : gift med Per

*/

Programmet simulerar ett frieri med ögonblicklig vigsel (Stockholmsäktenskap).

Mer om OO.

DD kapitel 8 innehåller en hel del mer om objekt, konstruktorer mm. Jag kommer bl a att tala en del om this, som är en speciell lokal variabel refererande objektet som varje exkverande instansmetod har. Slutet om hissar kan läsas kursivt eller sedan, avsnittet om paket behövs ej omedelbart.

Hemuppgifter redovisning v45.

1. CBA-hemuppgift : Vi ska implementera linjär-algebra-vektorer i planet med klassdefinition med enbart privata instanvariabler och många metoder för vektoralgebra.

Gör så här : Skapa lämpligen en egen katalog vectorOO för denna uppgift.

Modifiera klassen Vector.java från tidigare hemuppgift så den innhåller instansmetoder

motsvarande de klassmetoder som tidigar fanns i Vect. Detta kan göras snabbt om du lär dig använda Query Replace i Emacs-menyn Search.

Skriv om klassen TestVector så att den gör samma beräkningar som förut, jämför hur TestComplex har skrivits på två olika sätt ovan.2. CBA-hemuppgift : Början till "Vick-spelet".

Vi skall simulera ett föremåls ("gadget") glidande på en friktionsfri platta som lutas i vinklarna alfa och beta kring kring huvudtröghetsaxlarna.

Klassen Gadget skall använda din klass Vector. Skriv klart klassen Gadget utgående från denna pseudokod:

public class Gadget {

private Vector p // Instansvariabel för förmålets läge på

// på plattan. Startläge (0.05 m, 0.05 m);

private Vector v // Instansvariabel för förmålets hastighet

// på plattan. Starthastighet (0 m/s, 0 m/s);

static final double g = 9.81; // m / (s*s)

// Metoden tick ändrar p och v från tidigare värde. Plattan som

//förmålet ligger på lutas vinklarna alpha och beta radianer

// under tiden delta t. Fysiken för att skriva tick, se nedan!

public void tick(double deltat, double alpha, double beta)

// Metod resetAt ger förmålet läget ip och hastigheten iv

public void resetAt(Vector ip, Vector iv)

// Metod getWhere returnerar förmålets läge

public Vector getWhere()

}

Fysiken för rörelsen beskrivs med ekvationer med vektorer i planet. I detta avsnitt skrivs

vektorer i planet med fetstil. Vi räknar hela tiden i SI-enheter, vinklar i radianer.

Från fysiken vet vi att det gäller för kraften och accelerationen att

F = (m*g*sin(alfa), m*g*sin(beta)

F = m*a

vilket ger

a = (g*sin(alfa), g*sin(beta))

Vi ändrar plattans lutning (vinklarna alfa och beta) enbart vid vissa distinkta tidpunkter med intervallet Dt. Låt oss se vad som händer från tiden t till tiden (t+Dt) (detta motsvarar ett anrop på tick).

Vid tiden t befinner sig föremålet på platsen p med hastigheten v.

Hastigheten vid tiden (t+Dt)

vn = v + Dt* a // + addition av vektorer * multiplikation med skalär

Medelhastigheten under tiden deltat

vm = 0.5*( v + vn)

Varur vi får nya läget

pn = p +Dt*vm

pn och vn skall alltså vara nya värden för instansvariablerna p och v efter ett anrop till tick .

Alla vektoroperationer vi behöver för att skriva Gadget finns redan i Vector.

Skriv till slut ett huvudprogram i klassen MoveGadget i vilket du instansierar ett föremål på ett plan. Därefter ska man gång på gång från tangentbordet kunna mata in lutningsvinklarna och i terminalfönstret få utskrivet föremålets läge. Testkör ditt program.

Min lösning kan testköras på /info/inda01/OO/CBA med

... >java MoveGadget.

UML-liknade-diagram för klassen Gadget:

3. B-hemuppgift : Lös uppgift 8.3 i DD sid 441, dvs skriv en klass Rational som kan räkna med bråk. Lämpliga metoder och lämpliga namn på metoderna står i uppgiften.

Klassen skall ha privata instansvariabler för täljare och nämnare och publika instansmetoder, t ex en instansmetod för att returnera ett bråk som är summan av bråket och ett annat bråk på

liknade sätt som vi gjort för komplexa tal och vektorer i planet.

För att förkorta bråket så mycket som möjligt har du användning av metoden för beräkning av största gemensamma divisor, se uppgift DD 6.40 sid 311 som vi tidigare löst både rekursivt och iterativt.

Skriv ett testprogram (applikation) som testar din implementering. Testprogrammet ska också

beräkna delsummor i den geometrisk serien 1 + 1/2 + 1/4 + 1/8 + ...

Min lösning kan testköras på /info/inda01/OO/B med ... >java TestRational

4. A-hemuppgift : Början till Schack-spelet.

Skriv en klass Path för att kunna skapa "drag" på ett rutmönstrat bräde. Vi numrerar

rutorna som vi numrerat matriser, dvs (0,0) är rutan överst till vänster (schackspelare har ett annat sätt att betekna rutor). Ett drag från (1,3) till (1,7) skapas som

Path dr = new Path(1, 3, 1, 7);

Man skall sedan kunna anropa returnerande instansmetoder för dr som

dr.getKind() och få svaret STRAIGHT eftersom draget håller sig på samma rad/kolonn. Java- definition av STRAIGHT se teorin ovan. Andra drag kan få getKind() att retrunera DIAGONAL / HORSE / NOMOVE / OTHERMOVE.

dr.getDirection()och få svaret DOWN. Andra drag kan få getDirection() att retrunera UP / NODIR. Denna metod kommer att användas för bönder som bara får gå nedåt (svarta bönder) eller uppåt (vita bönder) på brädet.

dr.getlength() och få svaret 4 eftersom draget dr är fyra rutor långt.

dr.toString() och få svaret "strait down 4 step(s) "

dr.getI1(), dr.getJ1(), dr.getI2(), dr.getJ2() returnerar de olika argumenten till konstruktorn, dvs i vårt fall 1, 3, 1, 7 respektive.

Skriv ett testprogram (applikation) som testar din implementering.

Min lösning kan testköras på /info/inda01/OO/A med

... >java TestPath

UML-liknade-diagram för klassen Path: