forelæsning uge 11 –mandag - users-cs.au.dk · pdf file– dokumentation og...
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