Versions Compared

Key

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

...

Expand
titleDel 4 – Arv (15%)

I denne delen skal du bruke arv for å håndtere ulike typer svar på en ryddigere måte. Det skal være én klasse for frie tekst-svar kalt StringQuestion, en klasse for tekst-svar med svar-alternativer kalt StringOptionsQuestion, og en klasse for ja/nei-spørsmål kalt BooleanQuestion. Det som er felles for disse klassene, f.eks. håndtering av selve spørsmålsteksten, skal samles i Question-superklassen. Bortsett fra at Question-klassen ikke skal kunne instansieres, så skal bruken av den være som i del 3, inkludert de to askQuestion- og checkQuestion-metodene fra del 2. Prøv å strukturere klassene dine, så det blir minst mulig duplisert kode. Du står selvsagt fritt til å definere andre metoder som trengs i løsningen din.

 En del av koden vil være lik tidligere kode. Du kan selv velge om du vil skrive den på nytt, eller beskrive presist hvordan tidligere skrevet kode(tekst) kopieres inn i de nye klassene.

Oppgave a)

 Implementer først Question-superklassen og de tre klassene StringQuestion, StringOptionsQuestion og BooleanQuestion kun med konstruktører.

Expand
titleLF

 Her er det viktig å identifisere at Question-klassen bør være abstrakt og deklarere både askQuestion- og checkAnswer (abstrakt). Question-klassen bør ha en konstruktør for å sette spørsmålet og implementere askQuestion. Subklassene må ta inn spørsmålet i sine konstruktører og bruke super(…) for å sette spørsmålet. Koden forøvrig bør være omtrent den samme som i del 2.

Code Block
public abstract class Question {
	private String question;
	protected Question(String question) {
		this.question = question;
	}
	public void askQuestion(PrintStream out) {
		out.println(question);
	}
	
	public abstract boolean checkAnswer(String answer);
}
public class StringQuestion extends Question {
	private String answer;
	
	public StringQuestion(String question, String answer) {
		super(question);
		this.answer = answer;
	}
	@Override
	public boolean checkAnswer(String answer) {
		return this.answer.equals(answer);
	}
}

Vi har her valgt å arve fra StringQuestion, for å gjenbruke mest mulig logikk.

Code Block
public class StringOptionsQuestion extends StringQuestion {
	private List<String> options;
	
	public StringOptionsQuestion(String question, String answer, Collection<String> options) {
		super(question, answer);
		this.options = new ArrayList<String>(options);
		if (! options.contains(answer)) {
			throw new IllegalArgumentException("Svaret er ikke et av alternativene!");
		}
		this.options = new ArrayList<String>(options);
	}
	@Override
	public void askQuestion(PrintStream out) {
		super.askQuestion(out);
		int num = 1;
		for (String option : options) {
			out.println(num + ". " + option);
			num++;
		}
	}
	@Override
	public boolean checkAnswer(String answer) {
		try {
			int num = Integer.valueOf(answer);
			if (this.answer.equals(options.get(num - 1))) {
				return true;
			}
		} catch (IndexOutOfBoundsException e) {
		} catch (NumberFormatException e) {
		}
		return super.checkAnswer(answer);
	}
}

Denne klassen bør passe på å bruke boolean for å lagre svaret.

Code Block
public class BooleanQuestion extends Question {
	
	private boolean answer;
	public BooleanQuestion(String question, boolean answer) {
		super(question);
		this.answer = answer;
	}
	@Override
	public boolean checkAnswer(String answer) {
		return (this.answer ? "ja" : "nei").equals(answer);
	}
}

 

Oppgave b)

 Implementer de to metodene askQuestion og checkAnswer, slik at alle Question-objekter (egentlig instanser av en av de tre andre klassene) i praksis virker som i del 2.

Expand
titleLF

Løsningen er vist over.

 

Oppgave c)

 Reimplementer metoden i Quiz for for innlesing av spørsmål fra fil, slik at den virker med de nye Question-subklassene.

Expand
titleLF

 Her er det stor sett samme kode som i del 3, men merk at en må instansiere de ulike subklassene til Question og vite når de ulike variantene skal brukes. Det er greit å referere til tidligere skrevet kode, for å spare tid og plass.

Code Block
public void init(Reader input) throws IOException {
	BufferedReader reader = new BufferedReader(input);
	while (reader.ready()) {
		String question = reader.readLine();
		if (question == null || question.trim().length() == 0) {
			break;
		}
		String answer = reader.readLine();
		Collection<String> options = new ArrayList<String>();
		while (reader.ready()) {
			String line = reader.readLine();
			if (line == null || line.trim().length() == 0) {
				break;
			}
			options.add(line);
		}
		if (answer.equals("ja") && options.size() == 0) {
			addQuestion(new BooleanQuestion(question, true));
		} else if (answer.equals("nei")") && options.size() == 0) {
			addQuestion(new BooleanQuestion(question, false));
		} else if (options.size() > 0) {
			addQuestion(new StringOptionsQuestion(question, answer, options));
		} else {
			addQuestion(new StringQuestion(question, answer));
		}
	}
}
Expand
titleDel 5 – Trinnvis utførelse (15%)

 I denne delen skal Quiz-klassen endres slik at den holder rede på tilstanden til en runde med spørsmål og svar, men lar en annen (hovedprogram)klasse styre fremdriften. Følgende tre metoder skal brukes til å styre fremdriften:

  • start(boolean mode, PrintStream out, InputStream in): starter quiz-en (men ingen spørsmål stilles ennå). mode-argumentet angir om et galt besvart spørsmål gjentas med en gang (mode=false), eller etter at alle etterfølgende spørsmål er stilt (mode=true). out-argumentet er strømmen som spørsmål skal skrives til (gjøres i doQuestion). in-argumentet er strømmen som svarene skal leses fra (gjøres også i doQuestion).

  • doQuestion(): Stiller ett (neste) spørsmål og leser ett svar(forsøk). Hvilket spørsmål som stilles avgjøres av hvilke som ennå ikke er stilt og besvart riktig og mode-verdien som ble gitt til start-metoden. Metoden returnerer antall spørsmål som ennå ikke er riktig besvart.

  • stop(): stopper quiz-en og returnerer hvor mange spørsmål som ble riktig besvart.

 Følgende kode eksemplifiserer hvordan (denne versjonen av) Quiz-klassen er ment å bli brukt:

Code Block
Quiz quiz = new Quiz();
// initialiser fra fil her (ikke vist)
quiz.start(true, System.out, System.in); // start quiz
while (quiz.doQuestion() > 0); // still spørsmål så lenge flere gjenstår
quiz.stop();

Implementer start-, doQuestion- og stop-metodene.

Expand
titleLF

Her er det viktig å skjønne at en må lagre argumentene til start-metoden, så en kan bruke dem siden, og skjønne hvordan en skal representere og håndtere gjenstående spørsmål iht. mode-verdien. Her tillater vi litt mer skissemessig kode, siden det sentrale er å skjønne hva som må huskes på tvers av kallene til start og doQuestion.

Code Block
private boolean mode;
private PrintStream out;
private Scanner scanner;
private int correctCount;
private List<Question> remaining;

public void start(boolean mode, PrintStream out, InputStream in) {
	this.out = out;
	scanner = new Scanner(in);
	correctCount = 0;
	remaining = new ArrayList<Question>(questions);
}

public int doQuestion() {
	Question question = remaining.remove(0);
	question.askQuestion(out);
	String answer = scanner.nextLine();
	if (question.checkAnswer(answer)) {
		System.out.println("Riktig");
		correctCount++;
	} else {
		System.out.println("Feil");
		remaining.add(mode ? remaining.size() : 0, question);
	}
	return (remaining != null ? remaining.size() : -1);
}

public int stop() {
	scanner.close();
	return correctCount;
}
Expand
titleDel 6 – Grensesnitt (10%)

Det er ønskelig å kunne støtte ulike filformat for quiz-er. Forklar med tekst og kode hvordan du vil bruke Java-grensesnitt for å gjøre det enkelt å bytte mellom ulike format og hvordan filinnlesingskoden du allerede har skrevet kan utgjøre (implementasjonen av) standardformatet. Merk at du ikke skal implementere nye format, kun vise hvordan det lett kan gjøres.

Expand
Code Block
public interface QuizFormat { public Collection<Question> read(Reader input) throws IOException; } public class StandardQuizFormat implements QuizFormat { public Collection<Question> read(Reader input) throws IOException { Collection<Question> questions = new ArrayList<Question>(); // // kode som i init-metoden, // men nye spørsmål legges til den interne questions-lista // return questions; } }

I Quiz:

Code Block
private QuizFormat quizFormat = new StandardQuizFormat();

public void setQuizFormat(QuizFormat quizFormat) {
	this.quizFormat = quizFormat;
}

public void init(Reader input) throws IOException {
	questions.addAll(quizFormat.read(input));
}
Expand
titleAppendix

Fra Math-klassen: float java.lang.Math.signum(float f) Returns the signum function of the argument; zero if the argument is zero, 1.0f if the argument is greater than zero, -1.0f if the argument is less than zero. Fra PrintStream-klassen: java.io.PrintStream A PrintStream adds functionality to another output stream, namely the ability to print representations of various data values conveniently. void java.io.PrintStream.print(String s) Prints a string. If the argument is null then the string "null" is printed. Otherwise, the string's characters are converted into bytes according to the platform's default character encoding, and these bytes are written in exactly the manner of the write(int) method. void java.io.PrintStream.println(String x) Prints a String and then terminate the line. This method behaves as though it invokes print(String) and then println().

 

Quiz quiz = new Quiz();

// initialiser fra fil her (ikke vist)

 quiz.start(true, System.out, System.in); // start quiz

 while (quiz.doQuestion() > 0); // still spørsmål så lenge flere gjenstår

 quiz.stop();