Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Expand
titleDel 1 - Småplukk (25%)

I pakken stuff vil du finne en del mindre oppgaver. Disse henger ikke sammen. De tre deloppgavene gir 10-15%, totalt 40% av eksamen.

Expand
titleOppgave 1: Enkel klasseoppbygging og predikater

I stuff finner du skallet til to klasser, Movie og MovieRegister. Les klassebeskrivelsen under nøye.

Movie har følgende interne tilstand (her vil du ikke finne noe beskrivelse i javadocen):

  • tittelen til filmen. Denne kan ikke være null, og skal kunne hentes ut med metoden getTitle().
  • hvor mange ganger den er sett, skal kunne hentes ut med metoden getTimesWatched(). Økes med en hver gang man har sett filmen, oppdatert med metoden watch().
  • hvor god var filmen, heltall fra 1-6. Alle filmer trenger ikke ha en rating. Skal kunne hentes ut med metoden getRating()

Det er ikke nødvendig å implementere flere metoder enn de som trengs for fylle kravene gitt over. Main-metoden i Movie viser noen eksempler på bruk av tester mot passende kode.

MovieRegister inneholder en samling med Movie-objekter. Følgende metoder finnes:

  • addMovie(Movie movie): Mulighet til å legge til nytt Movie-objekt
  • filterMovies(Predicate pred): Returnere filmene som tilfredsstiller kravene beskrevet i predikatet pred.
  • findMovie(String title): Returnere filmen med denne tittelen, eller null hvis filmen ikke finnes.
  • watch(String title): Se filmen med denne tittelen. Øker antallet ganger filmen er sett med 1. Hvis tittelen ikke finnes i registeret skal metoden utløse en IllegalStateException.
Code Block
languagejava
titleMovie
collapsetrue
package stuff;

import static org.junit.Assert.assertEquals;

public class Movie {

	// See the README file for a description of what is required for this file.
	private String title;
	private int timesWatched;
	private Integer rating;
	
	public Movie(String title) {
		if (title == null) throw new IllegalArgumentException("Title must not be null");
		this.title = title;
		this.timesWatched = 0;
		this.rating = null;
	}
	
	public String getTitle() {
		return title;
	}
	
	public int getTimesWatched() {
		return timesWatched;
	}
	
	public Integer getRating() {
		return rating;
	}
	
	public void setRating(Integer rating) {
		this.rating = rating;
	}
	
	public void watch() {
		timesWatched += 1;
	}

	public static void main(String[] args) {

//		Movie db = new Movie("Das Boot");
//		assertEquals(0, db.getTimesWatched());
//		assertEquals("Das Boot", db.getTitle());
//		
//		db.watch();
//		assertEquals(1, db.getTimesWatched());
//		
//		assertEquals(null, db.getRating());
//		db.setRating(4);
//		assertEquals(4, (int)db.getRating());
	}

}



Code Block
languagejava
titleMovieRegister
collapsetrue
package stuff;

import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class MovieRegister {


	// Add internal variables
	private Collection<Movie> movies = new ArrayList<>();


	/**
	 * Add movie to register
	 * @param movie
	 */
	public void addMovie(Movie movie) {
		movies.add(movie);
	}

	/**
	 * 
	 * @param title
	 * @return the movie with matching title, or null if no such movie exists.
	 */
	Movie findMovie(String title) {
		return movies.stream().filter(m -> m.getTitle().equals(title)).findAny().orElse(null);
	}

	/**
	 * Filter all registered movies based on a Predicate, and return them as a Collection.
	 * @param pred is the filter for which movies to watch
	 * @return A collection of movies testing true to pred.
	 */
	Collection<Movie> filterMovies(Predicate<Movie> pred) {
		return movies.stream().filter(pred).collect(Collectors.toList());
	}

	/**
	 * Watch movie 'title'.
	 * @param title
	 * @throws IllegalStateException if the title does not exist.
	 */
	public void watch(String title) {
		try {
			findMovie(title).watch();
		} catch (NullPointerException e) {
			throw new IllegalStateException("Movie does not exsits!");
		}
	}

	/**
	 * Small example of use of the class. Does NOT necessarily cover all uses of methods specified in assignment. 
	 * @param args
	 */
	public static void main(String[] args) {

				MovieRegister cb = new MovieRegister();
				cb.addMovie(new Movie("Das Boot"));
				cb.watch("Das Boot");
				System.out.println("Should be 1: " + cb.findMovie("Das Boot").getTimesWatched());

	}

}


Her skulle kandidatene vise at de behersket basisbegrepene i Java og objektorientering. Det var et par ulike småting som kompliserte bildet. Movie skulle holde koll på tre ting: tittelen, rating og hvor mange ganger en har sett på den. Disse tre verdiene hadde ulike bruksmåter. Tittel MÅTTE man ha - så da var det lurt å bare ha en konstruktør som tok inn en tittel. Rating skulle være 1 - 6, men også null dersom man ikke hadde sett den. Her var det ulike løsninger, noen valgte strenger mens andre brukte Integer. Like bra, vi ga ingen krav her. Hvor mange ganger en hadde sett den ble løst ved en teller som starter med 0.

I MovieRegister la vi også inn predikat, i en slik typisk filtreringsmetode. Her var det helt greit å løse den med for-løkker, men som vanlig sparer en mye tid hvis en bruker Collection sin stream.


Expand
titleOppgave 2 - testing

Klassen AverageComputer lar en registrere mange heltallsverdier og beregne gjennomsnittet av dem. Det følger med en testklasse, AverageComputerTest, som tester et enkelt tilfelle (beregning av snittet av tallene 3, 4 og 5).

Tilsynelatende virker klassen fint, men den inneholder to feil, den ene knyttet til beregning av gjennomsnitt, den andre til innkapslingen. Ingen av disse fanges opp av testklassen. Oppgaven går ut på å forbedre testklassen slik at begge feilene rapporteres på en hensiktsmessig måte. Du kan endre den eksisterende testmetoden og evt. legge til nye.

Code Block
languagejava
titleAverageComputerTest
collapsetrue
package stuff;

import static org.junit.Assert.assertEquals;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.junit.Assert;
import org.junit.Test;

public class AverageComputerTest {

	private static final double epsilon = 1e-8;

	@Test
	public void testComputeAverage() {
		final AverageComputer ac = new AverageComputer(Arrays.asList(3, 4, 5));
		Assert.assertEquals(4.0, ac.computeAverage(), epsilon);
	}

	// We can manipulate the list by adding to it from outside after 
	// it has been added to the method.
	@Test
	public void testNewComputerNotModifiedByChangingList() {
		List<Integer> intVals = new ArrayList<>();
		intVals.addAll(Arrays.asList(2, 3, 1, 4));
		AverageComputer avg = new AverageComputer(intVals);

		assertEquals(2.5, avg.computeAverage(), epsilon);
		intVals.add(5);
		assertEquals(2.5, avg.computeAverage(), epsilon);
	}

	// No cast from integer, thereby messing up those numbers...
	@Test
	public void testRetValIsCastBeforeDivide() {
		AverageComputer ac = new AverageComputer(Arrays.asList(3, 4));

		assertEquals(3.5, ac.computeAverage(), epsilon);
	}

}


Her var det i utgangspunktet to ulike feil vi var ute etter:

  • Mangel på innkapsling: Man kunne sende inn en liste, og så manipulere denne fra utsiden etterpå.
  • Mangel på cast til double ved beregning av gjennomsnitt: En typisk feil, da ulike programmeringsspråk gjør dette litt ulikt. I Java må du caste for å ikke få et avrundet svar.

I den grad det ble definert andre typer feil, så kunne det også gi poeng.


Expand
titleFXML

Filen Math.fxml definerer et grensesnitt der brukeren skal kunne:

  • skrive inn to flyttall i to tekstfelt
  • velge en regningsmetode fra en nedtrekksmeny (pluss, minus, gange, dele)
  • trykke på en knapp som så beregner resultatet av regnestykket og skriver svaret inn i et felt
  • trykke på en annen knapp som legger inn tilfeldige heltall mellom 1 og 100 i begge tekstfeltene

Du skal gjøre utvidelser og endringer i filen MathController.java som gjør at kravene nevnt over oppfylles. Er alle metoder som må finnes allerede definert? Grensesnittet kan sees ved å kjøre filen MathApp.java.

Slik ser FXML-applikasjonen ut:




Code Block
languagejava
titleMathController
collapsetrue
package stuff;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Random;

import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;

public class MathController {
	
	Random rand;

	@FXML
	private ComboBox<Character> typeSelector;

	@FXML
	private TextField firstField;

	@FXML
	private TextField secondField;
	
	@FXML
	private TextArea resultArea;

	@FXML
	private void initialize() {
		rand = new Random();
		Collection<Character> tmp = new ArrayList<>();
		tmp.add('+');
		tmp.add('*');
		tmp.add('/');
		tmp.add('-');
		typeSelector.getItems().addAll(tmp);
		typeSelector.getSelectionModel().select(0);
	}


	/**
	 * Gather doubles from two textfields, apply a mathematical method, and update a text component.
	 */
	@FXML
	private void onCalculate() {
		double res;
		double val1 = Double.parseDouble(firstField.getText());
		double val2 = Double.parseDouble(secondField.getText());
		switch (typeSelector.getValue()) { // Could use chained if-elseif-else instead of switch
			case '+':
				res = val1 + val2;
				break;
			case '-':
				res = val1 - val2;
				break;
			case '*':
				res = val1 * val2;
				break;
			case '/':
				res = val1 / val2;
				break;
			default:
				throw new IllegalArgumentException("Unexpected value: " + typeSelector.getValue());
		}
		
		resultArea.setText(Double.toString(res));
	}

	
	// Is there a method missing here? Where could it be?
	@FXML
	private void randomizeNumbers() {
		firstField.setText(Integer.toString(rand.nextInt(100) + 1));
		secondField.setText(Integer.toString(rand.nextInt(100) + 1));
	}
}

Her var en god del ferdig laget. En detalj var at det var et hint om at noe manglet i MathController - en måtte gå inn i fxml-filen, se at det manglet en metode for randomizeNumbers. Så den måtte lages, og inni der måtte en altså legge to tilfeldige heltall inn i firstField og secondField. Selve oppskriften på tilfeldig tall burde være grei (i en åpen bok-eksamen, spesielt!). Når det gjelder onCalculate, så var det mange måter å filtrere hvordan en skal håndtere regneartene. Alle var like gode, så lenge de faktisk virket. Men en måtte altså huske på å caste verdiene fra firstField og secondField før en bruker dem.


...