Senaste Labben
Senaste Labben Strålande resultat (mv 4.6)!
Senaste Labben Strålande resultat (mv 4.6)! God objektorientering?
Senaste Labben Strålande resultat (mv 4.6)! God objektorientering? God kodhygien?
Senaste Labben Strålande resultat (mv 4.6)! God objektorientering? God kodhygien? Använd breakpoints vid testning
Klasser och objekt All kod vi skriver ligger i klasser Klasser kan instansieras till objekt med new Variabler lagrar referenser till objekt Objekten har de instansvariabler och metoder som anges i motsvarande klassdefinition. Man kommer åt dem med punktnotation objektreferens. metod Alltså måste man alltid instansiera ett objekt för att kunna använda metoderna och variablerna Undantag: variabler och metoder får deklareras static. I så fall hör de direkt till klassen och man kommer åt dem med klassnamn. metod
God objektorientering Varje objekt svarar mot något substantiv i en beskrivning Logisk uppdelning av vilka objekt som kan göra vad Instansvariabler lagrar objektets tillstånd Objekt ändrar inte instansvariabler i andra objekt. Objekt kommunicerar genom att anropa varandras metoder, och kan skicka data genom metodernas parametrar. Objekt kan referera till andra objekt genom att lagra referenser i instansvariabler. Data som är gemensamma för alla objekt i en klass får deklareras static
God kodhygien Bra variabelnamn. Bra kommentarer. Bra uppdelning i små gripbara delar Bra indentering och radbrytningar
Overloading Vi kan ha flera olika metoder med samma namn Dessa måste i så fall skiljas genom olika signaturer: parametrarnas typ och antal int sum (int n1, int n2) { return n1+n2 int sum(int n1, int n2, int n3) { return n1+n2+n3 Detta används oftast för likartade uppgifter på olika parametertyper (tex println()), men metoderna kan vara hur olika som helst Gott råd: undvik overloading. Det förvirrar mest.
Arv (inheritance) Givet en klass A kan vi definiera en underklass B med ordet extends i klasshuvudet. Underklassens objekt ärver då allt från överklassen: class A { int x; void f(int z) {... class B extends A { int y;... b = new B(); if (b.x == b.y)... c = b.f(0);
Arv (inheritance) Givet en klass A kan vi definiera en underklass B med ordet extends i klasshuvudet. Underklassens objekt ärver då allt från överklassen: class A { int x; void f(int z) {... class B extends A { int y;... b = new B(); if (b.x == b.y)... c = b.f(0);
class A class B extends A p y g f z h p g f new B() h y z
Användning I swing finns ett stort antal klasser lämpade för grafik. Tex JFrame som definierar en klass för fönster. Vi kan bilda en underklass till JFrame som svarar mot mer specialiserade fönster. Tex spelplaner för luffarschack. Vi får automatiskt alla fönstermetoder som JFrame har men kan definiera ytterligare sådana. Överklasser: allmänna Underklasser: mer specialiserade
Klasshierarkier En underklass kan i sig användas som överklass till en ännu mer specialiserad klass: class Business {... class RetailBusiness extends Business {... class KMart extends RetailBusiness {... Här ärver KMart både från RetailBusiness och Business
!"#$%&##' 3&,*$4!"#$%&##' /&+0$-&!"#$%&##' ()*+,' )*-.#' ($%12#' Varje klass har bara en överklass som den ärver från. Arv går i flera led. Om inget sägs är den speciella klassen Object överklass.
class A class B extends A class C extends B p y q g z v f h p g f new C() h y z q v
Overriding Om en metod som finns i överklassen definieras om i underklassen gäller definitionen i underklassen. Detta kallas overriding. class A { int x; void f(int z) {... class B extends A { int y; void f(int z) {... c = b.f(0);
Overriding Om en metod som finns i överklassen definieras om i underklassen gäller definitionen i underklassen. Detta kallas overriding. class A { int x; void f(int z) {... class B extends A { int y; void f(int z) {... c = b.f(0);
class A class B extends A p h g f z h p g f new B() h z
Exempel JPanel definierar en metod paintcomponent(). Alla underklasser till JPanel ärver denna. Skriver vi ingen ny sådan metod i underklassen så finns den ursprungliga (som ritar en tom yta). Vi kan göra en override och definiera en egen paintcomponent() som innehåller önskad grafik
final Om överklassen deklarerat en metod final så får man inte göra override på den i en underklass. class A { int x; final void f(int z) {... class B extends A { int y; void f(int z) {... Detta används för att hindra omdefinitioner av kritiska metoder i stora system
super Om man definierat om en metod i underklassen men ändå vill använda överklassens metod, använd super. class A { int x; void f(int z) {... class B extends A { int y; void f(int z) {... void g() { y = f(0); y = super.f(0);
super Om man definierat om en metod i underklassen men ändå vill använda överklassens metod, använd super. class A { int x; void f(int z) {... class B extends A { int y; void f(int z) {... void g() { y = f(0); y = super.f(0);
Exempel class A extends JPanel { override void paintcomponent(graphics g) { super.paintcomponent(g)...
Konstruktorer I underklassens konstruktor lägger Java alltid automatiskt in ett anrop till överklassens konstruktor först. class A { int z; A() { z = 0; class B extends A { int y; B() { y = 1; b = new B(); Här har b.z värdet 0 och b.y värdet 1 eftersom både As och Bs konstruktorer har använts
Om man vill får man i underklassens konstruktor göra ett anrop till överklassens konstruktor med notationen super(). Detta måste då ligga först i konstruktorn. class A extends JFrame { A() {... class A extends JFrame { A() { super( Titel );... Inget anrop till JFrames konstruktor. Då sker automatiskt ett anrop till JFrame(), dvs det skapas ett fönster utan titel Ett anrop till JFrames konstruktor med parametern Titel. Detta innebär ett anrop till JFrame( Titel ), dvs det skapas ett fönster med rubriken Titel.
Abstrakta klasser En abstrakt metod deklareras med ordet abstract och innehåller enbard metodhuvud. Ingen metodkropp! abstract void moveto(int x, int y); Detta anger metodens signatur men inte dess beteende. En klass som har abstrakta metoder måste deklareras abstract: abstract class Piece { abstract void moveto(int x, int y);...
En abstrakt klass får inte instansieras! mypiece = new Piece() Den enda användningen är som överklass till mer specialiserade klasser class King extends Piece {... Underklassen ger definitioner av alla överklassens abstrakta metoder class King extends Piece { void moveto (int i, int j) {...
abstract class A class B extends A p g g f z h p g f new B() h z
En abstrakt klass får innehålla både vanliga och abstrakta metoder. Om någon metod är abstrakt är klassen abstrakt. Om en underklass inte implementerar alla abstrakta metoder blir underklassen också abstrakt.
abstract class A abstract class B extends A p g z f h new B()
I en abstrakt klass får alla metoderna vara abstrakta. Klassen säger då ingenting om något beteende alls, utan bara om en signatur: vilka metoder som ska finnas och vilka typer de och deras parametrar har. En signatur är precis det som man behöver veta om en klass för att kunna anropa metoderna. För att definiera signaturer är det bättre att använda interface än abstrakta klasser
interface Interface används för att definiera signaturer. Ett interface ser ut som en klassdeklaration men med ordet interface istället för class. Den innehåller bara abstrakta metoder. Metoderna blir automatiskt abstrakta - man behöver inte skriva abstract
interface A f g interface A { void f (int i); boolean g(); String h(int i, int j); h new A() Liksom en abstrakt klass får ett interface inte instansieras.
implements För ett interface används ordet implements (och inte extends): om A är ett interface kan man skriva class B implements A Eftersom A är ett interface och inte en klass finns inget att ärva. Allt i A är abstrakt. Istället gör ordet implements ett åtagande: alla abstrakta metoder i A måste implementeras i B. Det får dessutom finnas ytterligare saker i B, men åtminstone metoderna i A måste finnas. implements är alltså ett sätt att i klasshuvudet ange en del av klassens signatur
class B interface A implements A f g h p f g h
class B interface A implements A f g h p f g
En klass kan implementera flera interface och måste då implementera alla abstrakta metoder i interfacen interface A interface B interface C f g h p class D implements A, B, C f g h p
Exempel interface MouseListener { void mouseclicked(mouseevent event); void mousepressed(mouseevent event); void mousereleased(mouseevent event); void mouseentered(mouseevent event); void mouseexited(mouseevent event);
Exempel I klassen JPanel finns en metod addmouselistener() Den tar som enda parameter ett objekt som ska vara en muslyssnare För att det objektet verkligen säkert ska fungera måste det ha metoderna mouseclicked() etc. Alltså, när man skriver addmouselistener(m) måste man vara säker på att m har sådana metoder. Det är man om m är från en klass som implementerar interfacet MouseListener
implements vs extends Använd implements om interface och extends om (abstrakta) klasser En (abstrakt) klass får innehålla också riktiga metoder. Ett interface får bara ha abstrakta metoder. En klass får vara underklass till bara en annan klass En klass får implementera flera interface
interface A interface B abstract class C f g h p h q class D extends C implements A, B f g p
Exempel class MyPanel extends JPanel implements MouseListener, KeyListener { Vi definierar en klass som ärver JPanel. Det ger oss massor av metoder åt grafikhållet Vi måste definiera metodeterna mouseclicked(), keypressed() etc för att uppfylla interfacen. Vi får använda objekt instansierade av MyPanel som parametrar vid anrop till addmouselistener() och addkeylistener()
Encapsulation Stora programsystem består av flera filer, så kallade packages. En package kan användas på två sätt. 1) Instansiera objekt från klasser 2) Bilda underklasser I varje sådan package är det viktigt att bestämma: vad ska vara tillgängligt utanför, dvs av användare, och vad har vi bara för internt bruk. Det som är tillgängligt utanför måste deklareras public. För att förhindra overriding i en underklass använd final. För att ställa krav på användaren använd interface.
Dagens tillämpning Luffarschack: Hitta sätt att ranka rutor med avseende på hur bra det vore att sätta kryss eller ring där Grundide: det enda som är viktigt för en ruta är vilka möjliga vinnande rader den kan ingå i. Så vi vill räkna ut några sådana.
Vilka möjliga vinstrader är rutan (2,1) del i?
Vilka möjliga vinstrader är rutan (2,1) del i? (0,1)-(4,1)
Vilka möjliga vinstrader är rutan (2,1) del i? (0,1)-(4,1) (1,1)-(5,1)
Vilka möjliga vinstrader är rutan (2,1) del i? (0,1)-(4,1) (1,1)-(5,1) (2,1)-(6,2)
Vilka möjliga vinstrader är rutan (2,1) del i? (0,1)-(4,1) (1,1)-(5,1) (2,1)-(6,2) (2,0)-(2,4)
Vilka möjliga vinstrader är rutan (2,1) del i? (0,1)-(4,1) (1,1)-(5,1) (2,1)-(6,2) (2,0)-(2,4) (2,1)-(2,5)
Vilka möjliga vinstrader är rutan (2,1) del i? (0,1)-(4,1) (1,1)-(5,1) (2,1)-(6,2) (2,0)-(2,4) (2,1)-(2,5) (1,0)-(5,4)
Vilka möjliga vinstrader är rutan (2,1) del i? (0,1)-(4,1) (1,1)-(5,1) (2,1)-(6,2) (2,0)-(2,4) (2,1)-(2,5) (1,0)-(5,4) (2,1)-(6,5)
Vilka möjliga vinstrader är rutan (2,1) del i? (0,1)-(4,1) (1,1)-(5,1) (2,1)-(6,2) (2,0)-(2,4) (2,1)-(2,5) (1,0)-(5,4) (2,1)-(6,5) Totalt 7 st möjliga femradingar
Antag att spelet har pågått ett tag. En del rutor har, en del har O, och en del är tomma. Vi vill veta värdet av att sätta ett märke ( eller O) i en viss tom ruta. Det värdet beror av statusen på rutans femradingar.
Exempel. Rutans samtliga femradingar innehåller redan både och O. O O O I detta fall är det tämligen värdelöst att sätta ett märke här
Exempel. En femrading innehåller fyra kryss O O I detta fall är det jättebra att sätta ett märke här
För varje femrading, bestäm dess status med avseende på ett märke ( eller O) som så: Om det inte finns något motståndarmärke i femradingen kan den fortfarande vara en vinnande femrading. I så fall, låt dess rang vara antalet märken i raden. Om det finns ett motståndarmärke kan den femradingen aldrig vinna åt mig. Låt rangen bli -1. Exempel, med avseende på : 2 3 O -1 0 O -1 O -1
För en ruta, bestäm statusen av samtliga dess femradingar, med avseende på både och O. med avseende på : O
För en ruta, bestäm statusen av samtliga dess femradingar, med avseende på både och O. med avseende på : O (0,1)-(4,1): 2
För en ruta, bestäm statusen av samtliga dess femradingar, med avseende på både och O. med avseende på : O (0,1)-(4,1): 2 (1,1)-(5,1): 1
För en ruta, bestäm statusen av samtliga dess femradingar, med avseende på både och O. med avseende på : O (0,1)-(4,1): 2 (1,1)-(5,1): 1 (2,1)-(6,2): 1
För en ruta, bestäm statusen av samtliga dess femradingar, med avseende på både och O. med avseende på : O (0,1)-(4,1): 2 (1,1)-(5,1): 1 (2,1)-(6,2): 1 (2,0)-(2,4): 0
För en ruta, bestäm statusen av samtliga dess femradingar, med avseende på både och O. med avseende på : O (0,1)-(4,1): 2 (1,1)-(5,1): 1 (2,1)-(6,2): 1 (2,0)-(2,4): 0 (2,1)-(2,5): 1
För en ruta, bestäm statusen av samtliga dess femradingar, med avseende på både och O. med avseende på : O (0,1)-(4,1): 2 (1,1)-(5,1): 1 (2,1)-(6,2): 1 (2,0)-(2,4): 0 (2,1)-(2,5): 1 (1,0)-(5,4): -1
För en ruta, bestäm statusen av samtliga dess femradingar, med avseende på både och O. med avseende på : O (0,1)-(4,1): 2 (1,1)-(5,1): 1 (2,1)-(6,2): 1 (2,0)-(2,4): 0 (2,1)-(2,5): 1 (1,0)-(5,4): -1 (2,1)-(6,5): -1
För en ruta, bestäm statusen av samtliga dess femradingar, med avseende på både och O. med avseende på : O (0,1)-(4,1): 2 (1,1)-(5,1): 1 (2,1)-(6,2): 1 (2,0)-(2,4): 0 (2,1)-(2,5): 1 (1,0)-(5,4): -1 (2,1)-(6,5): -1 Summa rankade femradingar rank antal 0 1 1 3 2 1 3 0 4 0
För en ruta, bestäm statusen av samtliga dess femradingar, med avseende på både och O. med avseende på O : O
För en ruta, bestäm statusen av samtliga dess femradingar, med avseende på både och O. med avseende på O : O (0,1)-(4,1): -1
För en ruta, bestäm statusen av samtliga dess femradingar, med avseende på både och O. med avseende på O : O (0,1)-(4,1): -1 (1,1)-(5,1): -1
För en ruta, bestäm statusen av samtliga dess femradingar, med avseende på både och O. med avseende på O : O (0,1)-(4,1): -1 (1,1)-(5,1): -1 (2,1)-(6,2): -1
För en ruta, bestäm statusen av samtliga dess femradingar, med avseende på både och O. med avseende på O : O (0,1)-(4,1): -1 (1,1)-(5,1): -1 (2,1)-(6,2): -1 (2,0)-(2,4): 0
För en ruta, bestäm statusen av samtliga dess femradingar, med avseende på både och O. med avseende på O : O (0,1)-(4,1): -1 (1,1)-(5,1): -1 (2,1)-(6,2): -1 (2,0)-(2,4): 0 (2,1)-(2,5): -1
För en ruta, bestäm statusen av samtliga dess femradingar, med avseende på både och O. med avseende på O : O (0,1)-(4,1): -1 (1,1)-(5,1): -1 (2,1)-(6,2): -1 (2,0)-(2,4): 0 (2,1)-(2,5): -1 (1,0)-(5,4): 1
För en ruta, bestäm statusen av samtliga dess femradingar, med avseende på både och O. med avseende på O : O (0,1)-(4,1): -1 (1,1)-(5,1): -1 (2,1)-(6,2): -1 (2,0)-(2,4): 0 (2,1)-(2,5): -1 (1,0)-(5,4): 1 (2,1)-(6,5): -1
För en ruta, bestäm statusen av samtliga dess femradingar, med avseende på både och O. med avseende på O : O (0,1)-(4,1): -1 (1,1)-(5,1): -1 (2,1)-(6,2): -1 (2,0)-(2,4): 0 (2,1)-(2,5): -1 (1,0)-(5,4): 1 (2,1)-(6,5): -1 Summa rankade femradingar rank antal 0 1 1 1 2 0 3 0 4 0
O En sammanfattande ranking av rutan ges av följande tabell:
O En sammanfattande ranking av rutan ges av följande tabell: rank O 0 1 1 1 1 3 2 0 1 3 0 0 4 0 0
Uppgift Gör ett program som räknar ut en sådan tabell för varje tom ruta. Ska kunna anropas av ett godtyckligt luffarschacksgui. Demo
Vi gör en så kallad package som heter LufRank. Den har en public class som också heter LufRank. En användare kan instansiera ett objekt av den och få något som räknar ut rankingen. LufRank ska alltså ha en metod ranktable() som räknar ut rankingtabellen för en given ruta och given märkestyp. vad är signaturen för ranktable()?
ranktable() har följande parametrar: - koordinaten för rutan - märket ( eller O) - ett objekt som representerar ställningen på brädet, dvs kan tala om, för varje koordinat, om där finns ett märke och i så fall vilket. Dags att bestämma hur data representeras vid anrop av ranktable().
- koordinaten för rutan representeras som ett par av heltal (x och y). - märket representeras som ett heltal - brädet måste ha en metod findmark(), som givet en koordinat (dvs par av heltal) returnerar märket (dvs ett heltal), där noll betyder ledig. - denna metod är allt som LufRank kräver av brädet. Använd ett interface för att uttrycka det kravet. - resultatet av ranktable() blir en int[] som innehåller rankingen.
interface TwoDimensionalMarks { int findmark (int x, int y); public int [] ranktable(int x, int y, int mark, TwoDimensionalMarks board){... Initialt är resultatet en array där alla värden är 0 För alla femradingar som hör till rutan (x,y): räkna ut femradingens ranking om denna är >0 så öka motsvarande element i resultatet
Vi behöver metoder för att: 1. Räkna ut alla femradingar som hör till en viss koordinat 2. Räkna ut rankingen för en enskild femrading Börja med 2. Metoden rank() tar som indata en femrading, ett märke (dvs int), och ett spelbräde som innehåller ställningen. Den ska returnera -1 om femradingen innehåller något annat märke. Annars ska den returnera antalet märken. Nu verkar det vara dags att bestämma hur en femrading ska representeras!
En femrading består av en koordinat som representerar den ena ändpunkten, och en andra koordinat som representerar riktningen. Koordinater är par av heltal final Coordinate d0 = new Coordinate(1,0); final Coordinate d1 = new Coordinate(0,1); final Coordinate d2 = new Coordinate(1,1); final Coordinate d3 = new Coordinate(1,-1); final Coordinate[] directions = {d0,d1,d2,d3; class Coordinate { final int x,y; Coordinate (int x, int y) { this.x = x; this.y = y;
Klassen Coordinate innehåller dessutom en metod som kollar om koordinaten ligger på brädet. Här är brädets storlek SIZE x SIZEY rutor, där SIZE och SIZEY är instansvariabler av typen final int. Slutligen finns en metod neighbour() för att räkna ut vilken koordinat som ligger i en viss riktning och ett visst antal rutor bort boolean valid() { return x >= 0 && y >= 0 && x < SIZE && y < SIZEY; Coordinate neighbour (Coordinate direction, int distance) { return new Coordinate(x+distance*direction.x, y+distance*direction.y);
class Fiverow { final Coordinate start, direction; Fiverow (Coordinate b, Coordinate d) { start = b; direction = d; Coordinate element (int i) { return start.neighbour(direction,i); boolean valid() { return start.valid() && element(4).valid();
Metoden rank() tar som indata en femrading, ett märke (dvs int), och ett spelbräde som innehåller ställningen. Den ska returnera -1 om femradingen innehåller något annat märke. Annars ska den returnera antalet märken. int rank(fiverow fiverow, int mark, TwoDimensionalMarks board) { int res = 0; for (int i=0; i<5; i++) { Coordinate current = fiverow.element(i); int currentmark = board.findmark(current.x, current.y); if (currentmark==mark) res++; else if (currentmark!= 0) { res = -1; break; return res;
Initialt är resultatet en array där alla värden är 0 För alla femradingar som hör till rutan (x,y): räkna ut femradingens ranking om denna är >0 så öka motsvarande element i resultatet Anta att vi har en metod getrows() som räknar ut femradingar för en given koordinat. public int [] ranktable(int x, int y, int mark, TwoDimensionalMarks board) { int [] result = {0,0,0,0,0,0; for (Fiverow fiverow : getrows(new Coordinate(x,y))) { int numberofmarks = rank(fiverow, mark, board); if (numberofmarks >= 0) result[numberofmarks]++; return result;
Vi har nu gjort hela LufRank så när som på getrows(). Den kräver lite eftertanke. Slutsats 1: vi lagrar den som en ArrayList<Fiverow>. ArrayList (se L&L 7.7) har en metod add() för att lägga till element och vi kan iterera över den i for-satsen. (Mer än så behöver vi inte veta om ArrayList just nu.) Slutsats 2: getrows() kommer förmodligen att anropas mycket ofta för en given koordinat. Därför kan det löna sig att räkna ut det en gång för alla och lagra i en tvådimensionell array.
LuFRank får ytterligare en instansvariabel final ArrayList<Fiverow>[][] rows; getrows() gör helt enkelt en tabelluppslagning: ArrayList<Fiverow> getrows(coordinate base) { return rows[base.x][base.y]; I LufRank konstruktorn definieras rows med hjälp av metoden constructrows(). Konstruktorns parametrar är brädets storlek. public LufRank(int x, int y) { SIZE = x; SIZEY = y; rows = new ArrayList [SIZE][SIZEY]; constructrows();
constructrows() är nu allt som återstår. Den ska alltså, för alla i,j, göra en ArrayList av femradingar som hör till koordinaten (i,j), och lägga den i rows[i][j] Antag att vi har en metod allfiverows() som räknar ut alla femradingar för en viss koordinat. Då blir det lätt: void constructrows() { for (int i=0; i<size; i++) for (int j=0; j<size; j++) rows[i][j] = allfiverows (new Coordinate(i,j));
allfiverows() är nu allt som återstår. Den ska hitta alla femradingar till koordinaten (i,j). Antag att vi har en metod adddirection() som lägger till femradingar som går i en viss riktning till en lista. Då blir det lätt: ArrayList<Fiverow> allfiverows(coordinate base) { ArrayList<Fiverow> al = new ArrayList<Fiverow>(); for (Coordinate direction : directions) adddirection(base, direction, al); return al;
adddirection() är nu allt som återstår. Den ska lägga till alla femradingar i viss riktning till listan. Det finns fem stycken femradingar i varje riktning. Startpunkten får vi med ett offset från den nuvarande koordinaten. Antag att vi har en metod addone() som lägger till en femrading (om den ligger helt inom brädet) för en viss riktning och offset. Då blir det lätt: ArrayList<Fiverow> adddirection(coordinate base, Coordinate direction, ArrayList<Fiverow> al) { for (int offset = 0; offset < 5; offset++) addone(base, direction, offset, al); return al;
addone() är nu allt som återstår. Den ska lägga till en femrading i viss riktning och med visst offset till listan, om den femradingen ligger helt inom brädet. addone() är ju lätt: ArrayList<Fiverow> addone(coordinate base, Coordinate direction, int offset, ArrayList<Fiverow> al ){ Fiverow fiverow = new Fiverow(base.neighbour(direction, offset-4), direction); if (fiverow.valid()) al.add(fiverow); return al;
Då är allt klart
För att använda LufRank, skriv import LufRank.*; När du startar ett spel kan du skaffa dig ett rankingobjekt, tex kallat ranker, genom LufRank ranker = new LufRank(x,y); där x,y är storleken på brädet.
Du har förmodligen någon klass Board som innehåller ställningen på brädet. Den kan se ut hursomhelst, men se till att den implementerar interfacet TwoDimensionalMarks. Det innebär att den måste ha en metod public int findmark(int x, int y) som returnerar markeringen vid (i,j). För att få rankingtabellen vid (i,j) med avseende på markeringen mark och ställningen som ges av board, använd ranker.ranktable(i,j,mark,board)
Inför nästa lab Läs resten av L&L (ej kap 9-12) Behåll samma partner Se till att få kod från annan grupp Kolla upp hur man definierar en package i netbeans