forelæsning uge 11 –mandag - users-cs.au.dk · pdf file– dokumentation og...

43
Nedarvning En klasse kan være en subklasse af en anden Det betyder at subklassen arver superklassens feltvariabler og metoder Object klassen Superklasse for alle klasser Indeholder en række nyttige metoder, bl.a. getClass, toString, equals og hashCode Afleveringsopgave: Computerspil 2 Dokumentation og test af de klasser, som I har implementeret i den første delaflevering Forelæsning Uge 11 – Mandag

Upload: lyphuc

Post on 10-Feb-2018

215 views

Category:

Documents


1 download

TRANSCRIPT

• Nedarvning– En klasse kan være en subklasse af en anden– Det betyder at subklassen arver superklassens feltvariabler og metoder

• Object klassen– Superklasse for alle klasser– Indeholder en række nyttige metoder,

bl.a. getClass, toString, equals oghashCode

• Afleveringsopgave: Computerspil 2– Dokumentation og test af de klasser,

som I har implementeret i den førstedelaflevering

Forelæsning Uge 11 – Mandag

● Nedarvning (subklasser)

2Stort overlap,men også forskelle

Feltvariabler

Metoder

• Vi vil gerne modellere et simpelt nyhedssystem med to slags meddelelser– Almindelige tekstmeddelelse (MessagePost)– Fotomeddelelse (PhotoPost)

• Uden brug af nedarvning ser det sådan ud

Dublering af kode

3

• Også dublering i NewsFeed klassen– Den har to arraylister– Mange metoder

gennemløber begge lister(dubleret kode)

• MessagePost og PhotoPost ligner hinanden– Store dele af de to klassers Java kode er identiske– Det gør koden svær at overskue og vanskelig at vedligeholde– Der opstår let fejl og inkonsistens, f.eks. når man ændrer noget i den ene

klasse, men glemmer at rette i den anden

Det er endnu værre, hvis vi i stedet for to slags postings har mange forskellige slags

Brug af nedarvning (subklasser)

4

Fælles feltvariabler og metoder

Forskellige feltvariabler og metoder

Vi har nu kunén arrayliste i NewsFeed klassen

• Nemmere at overskue• Ingen kodedublering

postings

PhotoPost MessagePostMessagePost

Post

Superklasse

Subklasser

Bemærk formen på pilehovederne

• Vi kan i stedet lave en Post klasse, som har MessagePost og PhotoPost som subklasser– En subklasse arver alle feltvariabler og metoder fra den oprindelig klasse

Nedarvning i Javapublic class Post {

private String username;private long timestamp;private int likes;private ArrayList<String> comments;

public Post(String author) {username = author;timestamp = System.currentTimeMillis();likes = 0;comments = new ArrayList<>();

}// Methods omitted

} public class MessagePost extends Post {

private String message;

public MessagePost(String author,String text){super(author);message = text;

}// Methods omitted

} 5

4 feltvariabler

Konstruktør • Initialiserer de

4 feltvariabler

Angiver at klassen er en subklasse af Post

Feltvariabel• I tilgift til de 4,

der nedarvesKonstruktør• Initialiserer feltvariablen• Kalder superklassens

konstruktør, så de fire nedarvede feltvariabler kan blive initialiseret

Superklasse

Subklasse

public class Post {

private String username;private long timestamp;private int likes;private ArrayList<String> comments;

public Post(String author) {username = author;timestamp = System.currentTimeMillis();likes = 0;comments = new ArrayList<>();

}// Methods omitted

} public class MessagePost extends Post {

private String message;

public MessagePost(String author,String text){super(author);message = text;

}// Methods omitted

}

Access rettigheder

6

De 4 feltvariabler i superklassen er private• Subklassen kan (som alle

andre klasser) kun tilgå dem via public accessor og mutator metoder defineret i superklassen

public metoder fra superklassen kan kaldes i subklassen som om de var lokale• Uden brug af dot notation• methodName(args)

Superklasse

Subklasse

• Java kræver at første sætning i en subklasses konstruktør er et kald til superklassens konstruktør

• Hvis dette ikke er tilfældet, indsætter compileren et sådant kald (uden parametre)

• Alle objekt klasser bestemmer en type– Når en klasse B er en subklasse af klassen A, siger vi at typen B er en

subtype af typen A (som er en supertype af typen B)

• De steder, hvor der skal bruges et objekt af typen A, kan man i stedet bruge et objekt af en subtype B– Assignments– Parameterværdier– Returværdier– Elementer i objektsamlinger

Subtyper

A a; B b;

public A pop() {...return b;...

}

pip(b);

public void pip(A param) {...};

• Ovenstående er kendt som Liskovs substitutionsprincip– Vi har tidligere sagt at typerne

skal matche (for assignments, parametre, returværdier og objektsamlinger)

ArrayList<A> list;list.add(b);

– Mere præcist betyder det, at typen af udtrykket skal være den samme som variablens/parameterens type eller være en subtype heraf 7

a = b;

NewsFeed klassen

8

public class NewsFeed {

private ArrayList<Post> posts;

public NewsFeed() {posts = new ArrayList<>();

}public void addPost(Post post) {posts.add(post);

}public void show() {

for(Post post : posts) {post.display();System.out.println();

}}

}

Konstruktør• Initialiserer feltvariablen

Metode til indsættelse af postings• Bemærk at parameteren er af

typen Post• Argumenter af typen MessagePost

og PhotoPost er også lovlige

Metode til udskrift af alle postings• For-each løkken løber arraylisten

igennem og udskriver de enkelte elementer ved hjælp af display metoden fra Post klassen

Feltvariabel• Arrayliste med elementtypen Post• Elementerne i listen vil være af typen

MessagePost eller PhotoPost

Statisk og dynamisk type

9

public void show() {for(Post post : posts) {

post.display();System.out.println();

}}

• Lad os se lidt nærmere på show metoden fra foregående slide

• Den lokale variabel post er erklæret til at have typen Post– Vi siger derfor, at den statiske type for variablen post er Post

• De objekter som variablen post vil pege på er af typen MessagePost eller typen PhotoPost– Vi siger derfor, at den dynamiske type for post er

• MessagePost, når post peger på et MessagePost objekt• PhotoPost, når post peger på et PhotoPost objekt

• Opsummering– Den statiske type bestemmes af variablens erklæring– Den dynamiske type bestemmes af det objekt, som variablen pt peger på– Den dynamiske type er altid en subtype af den statiske type (eller identisk

med denne)

Typeskift (type cast)

10

Vehicle v1 = new Vehicle();Vehicle v2 = new Car();Vehicle v3 = new Bicycle();

Vehicle v = new Car();Car c = new Car();

v = c; // Lovligtc = v; // Ulovligt, compile-time fejl

c = (Car) v; // Lovligt

Type cast• Typen af udtrykket ændres til at være Car• Kun muligt, hvis den dynamiske type af v er Car (eller en subtype af Car)• Ellers får man en run-time fejl• Objektet v ændres ikke på nogen måde ved et type cast• Det eneste, der ændres, er typen af udtrykket på højresiden af assignmentet

Javas variabler er polymorfe• De kan antage værdier af

forskellig type• Polymorf ≈ kan antage

forskellige former

• En subklasse kan igen have subklasser, osv.

Nedarvning i flere niveauer

11

• Dog klassen er en– Direkte subklasse af Mammal klassen– Subklasse af Animal klassen (subklasse relationen er transitiv)– Subklasse af sig selv (subklasse relationen er refleksiv)– Direkte superklasse for Poodle og Dalmatian klasserne– Superklasse af sig selv (superklasse relationen er refleksiv og transitiv)

Klassediagram for brug af Comparable

Person StringPixel …

12

<<interface>>Collection

boolean add(E e)boolean contains(Object o)...

3 forskellige slags UML pile• Fuldt optrukne pile med lukket

hoved angiver nedarvning mellem klasser/interfaces

• Stiplede pile med lukket hoved angiver implementation af interface

• Stiplede pile med åbent hoved angiver brug (uses)

CollectionsT min(Collection<T> c)T max(Collection<T> c)void sort(List<T> l)...

<<interface>>Comparableint compareTo(T o)

<<interface>>

List …<<interface>>

Queue<<interface>>

Set

ArrayList HashSetLinkedList Adult Child

Fælles feltvariabler og metoder

Forskellige feltvariabler og metoder

● Metoder i subklasser

13

• Lad os nu kigge lidt nærmere på metoder i super- og subklasser– Post klassen har en display metode, der

udskriver information om klassens tilstand– display metoden har kun adgang til

feltvariabler i Post klassen– Hvad gør vi, hvis vi gerne vil udskrive

information om subklassernes feltvariabler?

• Sidebemærkning (om cohesion)– display metoden udskriver klassens tilstand– Det ville være bedre at have en toString

metode, der returnerer en tekststreng– Så kan man på kaldstedet selv

bestemme, hvad man vil gøre ved tekststrengen

Situationen er som vist nedenfor

14

public void display() {System.out.println( username );System.out.print(timeString( timestamp ));if( likes > 0) {

System.out.println(likes + " people like this.");}else {

System.out.println();}if( comments .isEmpty()) {

System.out.println("No comments.");}else {

System.out.println(comments.size() + " comment(s).");}

}public void show() {for(Post post : posts) {post.display ();System.out.println();

}}

Post

Newsfeed

Post klassens display metode kender ikke de feltvariabler, der ligger i subklasserne, og kan derfor ikke udskrive information om disse

xxx

Første løsningsforslag (virker ikke)

15

• Vi kan flytte display metoden til de to subklasser

public void show() {for( Post post : posts) {post.display ();System.out.println();

}} Newsfeed

– Metoderne har kun adgang til superklassens feltvariabler via acces metoder

– Det giver massiv kodedublering– Vi får en compile-time fejl i NewsFeed klassen

• Den lokale variabel post har Post som sin statiske type

– Oversætteren bruger altid den statiske type og kigger derfor (uden held) i Post klassen (og dens superklasser) for at finde display metoden

– Det hjælper ikke, at alle subklasserne har en display metode

• Kaldet af super behøver ikke at være i første linje og det kan helt mangle (uden at compileren indsætter det)

• Det er kun nødvendigt at bruge ordet super, når samme metodenavn bruges i super- og subklasse

Korrekt løsning

16

• Vi har både en display metode i superklassenog i de to subklasser– Vi undgår kodedublering– display metoderne i subklasserne kalder

display metoden i superklassen og har (via den)adgang til superklassens feltvariabler uden brugaf acces metoder

public void display() {super.display();System.out.println(message);

} MessagePost

public void display() {super.display();System.out.println(filename);System.out.println(caption);

} PhotoPost

Statiske type

17

• Oversætteren bruger den statiske type,dvs. typen fra variablens erklæring– Den lokale variabel post er erklæret til at være af typen Post– Oversætteren kigger derfor i Post klassen for at finde display

metoden (og finder den)

public void show() {for( Post post : posts) {post.display ();System.out.println();

}} Newsfeed

Dynamiske type

18

public void show() {for(Post post : posts) {post.display ();System.out.println();

}} Newsfeed

• Under programudførelsen bruges den dynamiske type,dvs. typen af variablens aktuelle værdi– Når den aktuelle værdi af post er en MessagePost

kaldes displaymetoden fra MessagePost– Når den aktuelle værdi af post er en PhotoPost

kaldes display metoden fra PhotoPost

• De to subklasser overskriver superklassens displaymetode med deres egne udgaver af metoden– På engelsk: override ≈ underkende/tilsidesætte– De nye metoder skal have samme signatur som den de overskriver– Når man overskriver en metode, bør man af hensyn til compileren (og

dem der læser koden) skrive @Override i linjen over metodesignaturen– Så får man en advarsel, hvis signaturerne ikke matcher

Simpelt eksempel (kun en klasse)

19

Vi bestemmer variablens dynamiske type Find det objekt, som variablen peger på Find objektets type (klasse)

display metoden søges i PhotoPost og findes der

• Ingen nedarvning• Ingen overskrivning

p.display();

PhotoPost p;

Nedarvning uden overskrivning

20

Vi bestemmer variablens dynamiske type Find det objekt, som variablen peger på Find objektets type (klasse)

display metoden søges i PhotoPost (uden held) display metoden søges i superklasserne (efter tur) og findes i Post

p.display();

PhotoPost p;

display

Nedarvning med overskrivning

21

Vi bestemmer variablens dynamiske type Find det objekt, som variablen peger på Find objektets type (klasse)

display metoden søges i PhotoPost og findes der

p.display();

Den statiske type er nu Post

Post p;

Dynamic method lookup (opsummering)

22

• Under udførslen findes den rigtige metode ved at– bestemme variablens dynamiske type– søge metoden i denne klasse eller i en superklasse af den

• Oversætteren har tjekket, at metoden findes i– variablens statiske type (eller en af dennes superklasser)

• Den dynamiske type er en subtype af den statiske type– Vi er derfor sikre på at finde metoden– Den er i den statiske type eller en af dennes superklasser– Kan være overskrevet i en subtype heraf

• Ovenstående er kendt som dynamic method lookup– Spiller en essentiel rolle i anvendelsen af subklasser– Tillader at de enkelte subklasser kan lave deres egne

specialtilpassede versioner af en metode– Eksempel på responsibility-driven design

Polymorfi

23

• Betyder at noget kan antage forskellige former

• Vi har tidligere set, at Javas variabler er polymorfe– De kan antage værdier af forskellig type

• Vi har nu set, at også Javas metodekald er polymorfe– Et metodekald kan aktivere metoder i forskellige klasser (typer)

public void show() {for(Post post : posts) {post.display ();System.out.println();

}} Newsfeed

Metodekaldet aktiverer sommetider display metoden i MessagePost og sommetider display metoden i PhotoPost

● Object klassen

24

• Object klassen indeholder en række nyttige metoder, som de øvrige klasser nedarver (eller overskriver)– getClass metoden fortæller, hvilken klasse objektet tilhører– toString metoden returnerer en tekstrepræsentation

af det pågældende objekt– equals metoden tjekker om to objekter "ligner hinanden"– hashCode metoden beregner en hashkode

• Alle klasser i Java er subklasser af Object klassen

getClass metoden og instanceof

25

• Ved hjælp af getClass metoden (fra Object klassen)– Metoden returnerer et objekt fra klassen Class– I et kørende program har hver type (også de primitive) et (og kun et)

tilhørende objekt i Class– Class er en parametriseret type og objektet for klassen A tilhører Class<A>

• Man kan undersøge et objekts klasse på forskellige vis• Ved hjælp af det reserverede ord instanceof

– Her testes om objektet tilhører den angivne klasse eller en subklasse herafVehicle v;Car c;

if( v instanceof Car ) {c = (Car) v;

}

– Man kan også skrive som vist nedenfor (hvor Person.class angiver Person klassens objekt i klassen Class<Person>)

if( x.getClass() == y.getClass() ) {...}

if( x.getClass() == Person.class ) {...}

– I begge tilfælde skal klasserne matche eksakt – det er ikke nok at den ene er en subklasse af den anden (som ved brug af instanceof)

toString metoden

26

• Metoden bruges bl.a. i print og prinln metoderne– Hvis argumentet til metoderne ikke allerede er en tekststreng, bruges

toString til at konvertere argumentet til en tekststreng– Det er også toString metoden, der bruges, når den ene side af en +

operation skal konverteres til en tekststreng

• I Object klassen er toString metoden defineret til at returnere– Person@19bb25a, hvor Person er klassens navn og 19bb25a er den

hexadecimale repræsentation af hashkoden (som vi vender tilbage til)– Hashkoden giver ikke ret meget interessant information– String klassen redefinerer metoden til at returnere værdien af

tekststrengen (i stedet for String@19bb25a)

• Jeres egne klasser bør også redefinere toString metoden– For personklassen kan man f.eks. returnere tekststrengen Cecilie:18,

hvor Cecilie er personens navn og 18 er personens alder– I køreprøveopgaverne blev I altid bedt om at redefinere toString metoden

(for den simple af klasserne)

public String toString() {

StringBuilder builder = new StringBuilder();

builder.append(username);

builder.append(timeString( timestamp ));if( likes > 0) {

builder.append(likes + " people like this.");}else {

builder.append('\n');}if( comments .isEmpty()) {

builder.append("No comments.");}else {

builder.append(comments.size() + " comment(s).");}return builder.toString();

}

StringBuilder klassen (fra java.lang)

27

• Alternativ til konkatenation af strenge ved hjælp af + operatoren– Lad os, som et eksempel, lave en toString metoden for Post klassen

public String toString() {

StringBuilder builder = new StringBuilder();

builder.append(username);

builder.append(timeString( timestamp ));if( likes > 0) {

builder.append(likes + " people like this.");}else {

builder.append( '\n' );}if( comments.isEmpty()) {

builder.append("No comments.");}else {

builder.append(comments.size() + " comment(s).");}return builder.toString();

}

Lokal variabel af type StringBuilder

Returner den opbyggede streng

ved hjælp af toString metoden

i StringBuilder

Linjeskift

Tilføj de ønskede strenge ved hjælp

af append metoden i StringBuilder

equals metoden

28

• Metoden bruges bl.a. i collections til at afgøre om et givet element allerede er indeholdt i en mængde (Set)– for en mængde vil et kald add(elem) kun tilføje elementet, hvis der ikke

eksisterer et element e i mængden, så elem.equals(e) er sand

• I Object klassen er equals metoden defineret ved hjælp af ==– Det betyder at e1.equals(e2) kun evaluerer til sand, hvis de to objekter er

identiske (e1 == e2)– Dette er ofte for restriktivt– String klassen redefinerer metoden til at tjekke om de to tekststrenge er

ens, dvs. indeholder de samme tegn (i samme rækkefølge)

• Jeres egne klasser bør også redefinere equals metoden– Det kræves at equals er en ækvivalensrelation Refleksiv: x.equals(x) for alle x Symmetrisk: x.equals(y) y.equals(x) for alle x,y Transitiv: x.equals(y) y.equals(z) x.equals(z) for alle x,y,z

Teknisk: x != null !x.equals(null) for all x

Redefinering af equals metoden• For Person klassen kan man definere equals som følger

public boolean equals(Object otherObject) {

if( this = = otherObject ) {return true;}

if( otherObject == null ) {return false;}

if( getClass() != otherObject.getClass() ) {return false;}

Person other = (Person) otherObject;

return name.equals(other.name) && age == other.age;}

Rigtig klasse?

Type cast

Sammenlign (udvalgte)

feltvariabler

null?

Optimalisering

• I subklassen Child kan man definere equals som følgerpublic class Child extends Person {private int noOfToys;...public boolean equals(otherObject) {if( !super.equals(otherObject) ) {return false;}return noOfToys == otherObject.noOfToys;

}...

} 29

FeltvariabelKald af equals i superklassen• korrekt klasse mv• superklassens

feltvariabler

Tjek af egne feltvariabler

hashCode metoden

30

• Metoden returnerer en hashkode for objektet– Hashkoder bruges som nøgler i såkaldte hashtabeller, f.eks. i klasserne

HashSet og HashMap– Det kræves, at hashcode er konsistent med equals x.equals(y) hashcode(x) == hashCode(y)

– Derudover bør hashCode være konstrueret således, at der for to forskellige objekter (! x.equals(y)) er lille sandsynlighed for at deres hashkoder kolliderer (hashCode(x) == hashCode(y))

• I Object klassen er hashCode metoden defineret som objektets memory adresse– Dermed er det nemt at se, at hashCode er konsistent med equals

• Hvis jeres egne klasser redefinerer equals metoden, vil det normalt også være nødvendigt at redefinere hashCode metoden– Dette gøres for at sikre at ovenstående betingelse stadig er opfyldt

hashCode("tea") = 't' + 'e' + 'a' = 116 + 101 + 97 = 314hashCode("tea") = 312 * 't' + 31 * 'e' + 'a' = 114704

hashCode("eat") = 'e' + 'a' + 't' = 101 + 97 + 116 = 314

Hashkoder for tekststrenge

31

• Det er en hel videnskab at definere hashkoder, hvor der er så få kollisioner som muligt– Lad os starte med at se, hvordan hashCode kan defineres i String klassen– s er en feltvariabel, der indeholder værdien af tekststrengen

public int hashCode() {int hc = 0;for( int i = 0; i < s.length(); i++) {hc = hc + s.charAt(i);

}return hc;

}

hashCode("eat") = 312 * 'e' + 31 * 'a' + 't' = 100184

hc = 31 * hc + s.charAt(i);

Hashkoder for jeres egne klasser

public int hashCode() {return 11 * name.hashCode()

+ 13 * new Integer(age).hashCode();}

}

• For Person klassen kan man definere hashCode som følger– Vi bruger kun de feltvariabler, som tjekkes i equals metoden– Primitive værdier konverteres først til deres wrapper type– Hashkoderne multipliceres med forskellige primtal før de adderes

• I subklassen Child kan man definere hashCode som følgerpublic class Child extends Person {private int noOfToys;...public int hashCode() {return super.hashCode()

+ 17 * new Integer(noOfToys).hashCode();}...

} 32

Hashkode for superklassens

feltvariablerHashkode for

egne feltvariabler

Feltvariabel

● Afleveringsopgave: Computerspil 2

33

• I den anden delaflevering skal I bruge nogle af de ting, som I har lært om dokumentation og test, på de fire klasser, som I har implementeret i den første delaflevering– Gennemgå de fire klasser, og tilføj dokumentation, således at den er

helt i top og følger de retningslinjer, der gives i BlueJ bogen (herunder specielt afsnit 6.11, afsnit 9.7 og Appendix I)

– Lav velvalgte regression tests for de fire klasser• I skal både teste, hvad der normalt sker, og hvad der sker i

specialtilfælde og tæt ved grænseværdier• Husk, at der skal være både positive og negative tests• Det er dog ikke nødvendigt at lave testmetoder for trivielle accessor

og mutator metoder, der blot returnerer/ændrer en enkelt feltvariabel uden at gøre andet

• Herudover skal I rette de fejl og mangler, som instruktoren har påpeget i jeres første delaflevering

/*** Represents a road (between to cities).* A road is one-directional. This means that we may have a road from* City A to City B, without having a road from City B to City A.* * @author Kurt Jensen. * @version April 2017.*/

public class Road implements Comparable<Road> {/** References to the cities connected by this Road */private City from, to;

/** Length of this Road. */private int length;

/*** Creates a new Road object.* @param from City where this Road starts.* @param to City where this Road ends.* @param length Length of this Road object.*/public Road(City from, City to, int length){

this.from = from;this.to = to;this.length = length;

}

/*** Returns a reference to the City where this Road starts.* @return from city.*/

public City getFrom(){return from;

}... 34

Dokumentation af Road klassen

Kommentar for klassen

Kommentar for feltvariablerne

Kommentar for konstruktøren

Kommentar for metoden

Husk også at indsætte kommentarer passende steder i kompleks kode

For testklasser og testmetode behøver I ikke at lave klasse-og metodekommentarer

35

Documentation view• Husk at kontrollere, at

resultatet er fornuftigt– Skal give relevant og

letlæselig information– Til brugere, der ikke

kender implementationen af jeres klasser.

@Testpublic void compareTo() {

Road road1 = new Road(cityA, cityB, 0);Road road2 = new Road(cityA, cityC, 0);Road road3 = new Road(cityB, cityA, 0);assertTrue(road1.compareTo(road2) < 0);...assertTrue(road2.compareTo(road1) > 0);...assertEquals(road1.compareTo(road1), 0);

}

import static org.junit.Assert.*;import org.junit.After;import org.junit.Before;import org.junit.Test;

public class RoadTest { private Country country1, country2;private City cityA, cityB, cityC;

@Beforepublic void setUp() {

country1 = new Country("Country 1", null);cityA = new City("City A", 80, country1);cityB = ...cityC = ...

}...

36

Fra den generelle Test Fixture kan I "stjæle"• import sætningerne• Feltvariablerne for landet og byerne• Erklæringerne i setUp metoden

(idet vi dog sætter netværket til null, da vi ikke skal bruge det)

For overskuelighedens skyld bør I fjerne de dele, som I ikke bruger

Testmetode for konstruktøren• Vi skaber et Road objekt og tjekker at

feltvariablerne initialiseres korrekt

Testmetode for compareTo• Vi skaber veje mellem byerne fra vores

Test Fxiture (setUp metode) og tjekker at compareTo returnerer værdier med korrekt fortegn

• I skal lave en testmetode for hver(ikke triviel) konstruktør/metode

• Hver testmetode tester flere forskellige ting (via forskellige assertions)

Test All:

Test of Road klassen

@Testpublic void constructor() {

Road road = new Road(cityA, cityB, 4);assertEquals(road.getFrom(), cityA);...

}

@Testpublic void hasArrived() {

assertFalse(pos.hasArrived());pos.move();assertFalse(pos.hasArrived());...pos.move();assertTrue(pos.hasArrived());

}

import ...

public class PositionTest { private Country country1;private City cityA, cityB;private Position pos;

@Beforepublic void setUp() {

country1 = new Country("Country 1", null);cityA = ...cityB = ...pos = new Position(cityA, cityB, 3);

}

37

Disse ting kan I (som før) stjæle fra den generelle Test Fixture • Sætningerne i de to grønne bokse

skal I selv tilføje• Fjern de ting i Test Fixturen, som

ikke bruges

Testmetode for konstruktøren• Vi tjekker at feltvariablerne initialiseres

korrekt

Testmetode for hasArrived metoden• Hvor mange gange skal move metoden

kaldes før hasArrived returnerer true?

• Når man har lavet et par optagelser af testmetoder og set, hvordan de ser ud, er det langt hurtigere at skrive testmetoderne selv

• Som før skal I lave en testmetode for hver (ikke triviel) konstruktør/ metode

• Hver testmetode tester flere forskellige ting (via forskellige assertions)

Test af Position klassen

@Testpublic void constructor() {

assertEquals(pos.getFrom(), cityA);......

}

Spilleren står i cityA og er på vej mod cityB, som kan nås i 3 skridt

Testmetode for move• Kald move metoden et antal gange og test hver

gang værdierne af distance og returværdi• Husk at teste tilfældet, hvor distance er nul

Testmetode for turnAroundA. move + turnAroundB. move + turnAroundC. turnAround

Efter hvert kald af turnAround tjekkes, at de 4 feltvariablerne i pos har de korrekte værdier

Test af Position klassen (fortsat)

A

B

C

38

@Testpublic void move() {

assertEquals(pos.getDistance(), 3); assertTrue(pos.move());assertEquals(pos.getDistance(), 2;...

}

@Testpublic void turnAround() {

pos.move();pos.turnAround();assertEquals(pos.getFrom(), cityB);...pos.move();pos.turnAround();assertEquals(pos.getFrom() == ......pos.turnAround();assertEquals(pos.getFrom() == ......;

}

• Hvis man højreklikker testklassen og vælger Test All får man

A

B

C

39

Assertions• Assertions kan skrives på flere forskellige måder

– assertEquals, assertTrue, eller assertFalse– Det er lige meget hvilken af de tre assert metoder, man bruger– assertEquals bruger (som navnet antyder) equals metoden til at

sammenligne de to værdier (af en Objekt type)

assertEquals(cityA, pos.getFrom());assertTrue(cityA.equals(pos.getFrom()));

assertEquals(false, pos.hasArrived());assertTrue(!pos.hasArrived());assertFalse(pos.hasArrived());

Disse tre sætninger er ækvivalente

Disse to sætninger er ækvivalente

• Bemærk at knappen Run Tests kun tester metoderne i de test klasser, der allerede er succesfuldt kompileret

@Testpublic void arrive(){

for(int i=0; i<1000; i++) { // Try different seedsgame.getRandom().setSeed(i); // Set seedint bonus = country1.bonus(80); // Remember bonusgame.getRandom().setSeed(i); // Reset seedint arrive = cityA.arrive(); // Same bonusassertEquals(arrive, ...);assertEquals(cityA.getValue(), ...);

cityA.reset();} 40

Test af metoder med tilfældige værdier• Når man skal teste arrive metoden i City klassen kan det være

nyttigt at resette den seed værdi som Random objektet bruger– På den måde kan man finde ud af hvad bonus metoden returnerer,

når den kaldes inde fra arrive@Testpublic void arrive(){

game.getRandom().setSeed(0); // Set seedint bonus = country1.bonus(80); // Remember bonusgame.getRandom().setSeed(0); // Reset seedint arrive = cityA.arrive(); // Same bonusassertEquals(arrive, ...);assertEquals(cityA.getValue(), ...);

}

Test at returværdi og værdien af value er korrekt

Man kan også proppe det hele ind i en for løkke og dermed teste mange forskellige seed værdier• Tager ikke meget

længere tid• Det er opsætningen af

Test Fixturen, der er dyr

Husk at resette byen mellem de enkelte tests

Som ovenfor, bortset fra, at seed nu resettes til i

@Testpublic void bonus() {

for(int seed = 0; seed < 1000; seed++) { // Try 1000 different seedsgame.getRandom().setSeed(seed);int sum = 0;Set<Integer> values = new HashSet<>();for(int i = 0; i < 10000; i++) { // Call method 10000 timesint bonus = country1.bonus(80);assertTrue(...<= bonus && bonus <=...); // Correct intervalsum += bonus;values.add(bonus);

}assertTrue(... < sum && sum < ...); // Average close to 40assertEquals(values.size(), ...); // All values returned

}}

@Testpublic void reset() {

cityA.arrive(); cityA.arrive(); cityA.arrive();cityE.arrive(); cityE.arrive(); cityE.arrive();int valueE = cityE.getValue(); // Remember value of cityEcountry1.reset();assertEquals(cityA.getValue(), 80); // cityA is resetassertEquals(cityE.getValue(), valueE); // cityE is not reset

}

41

Eksempler på testmetoder for Country

Man bør lave tilsvarende tests for grænsetilfældene, hvor bonus kaldes med parametrene 0 og 1

arrive kaldes 3 gange for hver bySå er man nogenlunde sikker på at byens værdi er ændret

• Nedarvning– Subklasser– Forskellen på en variabels statiske og dynamiske type– Subtyper og Liskovs substitutionsprincip– Typeskift (type cast)

• Metoder i subklasser– Dynamic method lookup i forbindelse med nedarvning og overskrivning

• Object klassen– Superklasse for alle klasser– Indeholder en række nyttige metoder, bl.a. getClass, toString, equals og

hashCode

• Afleveringsopgave: Computerspil 2– Dokumentation og test af de klasser,

som I har implementeret i deførste delaflevering

● Opsummering

Bemærk vores automatiske værktøj til debugningaf Computerspilsprojektet• Kan anvendes i delaflevering 1 og 3• Kan hjælpe med at finde de klasser, der får

testmetoderne (i CG1 opg. 10 og CG3 opg. 6) til at fejle

• Kan også hjælpe med at finde de metoder/ konstruktører, der fejler 42

Det var alt for nu….. … spørgsmål

43