Versions Compared

Key

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

Her ligger løsningsforslaget til eksamen 2021. I kombinasjon med kildekoden vil vi beskrive hva oppgaven spurt etter, samt Vi vil senere legge til ofte forekommende feil og misforståelser i kombinasjon med kildekoden .

På gitlab finner dere full kode til alle deler i oppgaven.

...

Expand
titleDel 5 - Observatør-Observert (20 %)

Ta utgangspunkt i LoyaltyUser-klassen. LoyaltyUser er en klasse som implementerer en bruker som kan samle statuspoeng, og som kan gå opp og ned i status basert på dette.

Lojalitetsprogrammet samarbeider med andre og det er derfor ønskelig at andre kan lytte på statusen til en bruker. Grensesnittet for dette er definert i StatusListener.

Oppgave A) Fyll ut nødvendige metoder og felt for å støtte lytting i LoyaltyUser klassen, for å gjøre det mulig å lytte på endring i statusen til et brukernavn.

Viktig:

  • En lytter vil kun være interessert i en status, og dersom lytteren registreres på nytt er det en ny status den skal lytte på.
  • Selv om lytteren kun er interessert i en status, vil den være interessert i både når brukeren oppnår denne statusen og når den mister den.

Følgende metoder skal endres/implementeres:

  • checkForStatusUpgrade() - sjekker om brukeren kvalifiserer seg til en endring i statusnivå. Dersom statusen endres bør den si ifra til alle lytterne.
  • addListener(StatusListener listener, String status) - Registrerer en lytter som lytter på status.
  • removeListener(StatusListener listener) - fjerner lytteren.
  • fireStatusChanged(String oldStatus, String newStatus) - Sier ifra til alle lytterne som lytter på enten oldStatus eller newStatus om at statusen er endret.
Expand
titleKodesskjelett del 5


Code Block
languagejava
public interface StatusListener {
	
	/**
	 * 
	 * @param username The username of the user
	 * @param oldStatus The old status of the user
	 * @param newStatus The new status of the user
	 */
	public void statusChanged(String username, String oldStatus, String newStatus);

}



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

public class LoyaltyUser {
	private String username;
	private int points;
	private String status;
	public static List<String> validStatuses = Arrays.asList("Basic", "Gold", "Silver", "Platinum");
	// TODO - Add any extra needed fields here

	public LoyaltyUser(String username) {
		this.username = username;
		this.status = "Basic";
	}

	public String getUsername() {
		return username;
	}

	public int getPoints() {
		return points;
	}

	public String getStatus() {
		return status;
	}

	/**
	 * Adds point to this user
	 * @param points the points to add. Can also be a negative number
	 */
	public void addPoints(int points) {
		this.points += points;
		this.checkForStatusUpgrade();
	}

	/**
	 * Checks whether the user qualifies for a status upgrade/downgrade.
	 * 
	 * TODO: If the user qualifies for a new status all observers interested in the new or old
	 * status should be notified
	 */
	public void checkForStatusUpgrade() {
		if (this.points <= 1000) {
			this.status = "Basic";
		}
		if (this.points > 1000) {
			this.status = "Silver";
		}
		if (this.points > 5000) {
			this.status = "Gold";
		}
		if (this.points > 10000) {
			this.status = "Platinum";
		}
	}

	/**
	 * Adds a listener that listens on when this specific status is obtained or
	 * lost. If the user has been previously added, the old status should be
	 * overridden and the listener should listen on the new status
	 * 
	 * @param listener The listener that will observe
	 * 
	 * @param status   The status the listener will listen to
	 * 
	 * @throws IllegalArgumentException If the status is not valid
	 */
	public void addListener(StatusListener listener, String status) {
		...

	}

	/**
	 * Remove the listener
	 * 
	 * @param listener The listener to remove
	 */
	public void removeListener(StatusListener listener) {
		...
	}

	/**
	 * Updates all listeners that were interested in either the old or the new
	 * status that the status of the user has changed. Observers should only be
	 * notified if oldStatus and newStatus is different
	 * 
	 * @param oldStatus The old status of the user
	 * 
	 * @param newStatus The new status of the user
	 */
	private void fireStatusChanged(String oldStatus, String newStatus) {
		...
	}
}


/* RentalCar listeners listens to changes in Status for all userNames.*/
public class RentalCarListener implements StatusListener {

	// TODO - Add any needed fields here

	@Override
	/**
	 * Method that should be called when a given userName has updated its status.
	 */
	public void statusChanged(String username, String oldStatus, String newStatus) {
		/// TODO
	}

	/**
	 * Get's the discount of a user. Should be a 100 if the user currently has Gold
	 * status, otherwise should be 0.
	 * 
	 * @param username The username of the user
	 * 
	 * @return The discount the user qualifies for.
	 */
	public int getDiscount(String username) {
		// TODO
		return 0;
	}
}



Expand
titleLF


Code Block
languagejava
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class LoyaltyUser {
	private String username;
	private int points;
	private String status;
	public static List<String> validStatuses = Arrays.asList("Basic", "Gold", "Silver", "Platinum");
	// TODO - Add any extra needed fields here
	private Map<StatusListener, String> statusListeners = new HashMap<>();

	public LoyaltyUser(String username) {
		this.username = username;
		this.status = "Basic";
	}

	public String getUsername() {
		return username;
	}

	public int getPoints() {
		return points;
	}

	public String getStatus() {
		return status;
	}

	/**
	 * Adds point to this user
	 * @param points the points to add. Can also be a negative number
	 */
	public void addPoints(int points) {
		this.points += points;
		this.checkForStatusUpgrade();
	}

	/**
	 * Checks whether the user qualifies for a status upgrade/downgrade.
	 * 
	 * TODO: If the user qualifies for a new status all observers interested in the new or old
	 * status should be notified
	 */
	public void checkForStatusUpgrade() {
		String oldStatus = this.status;

		if (this.points <= 1000) {
			this.status = "Basic";
		}
		if (this.points > 1000) {
			this.status = "Silver";
		}
		if (this.points > 5000) {
			this.status = "Gold";
		}
		if (this.points > 10000) {
			this.status = "Platinum";
		}
		if (oldStatus != this.status) {
			this.fireStatusChanged(oldStatus, this.status);
		}
	}
	
	private void checkIsValidStatus(String status) {
		if (!validStatuses.contains(status)) {
			throw new IllegalArgumentException("Invalid status");
		}
	}

	/**
	 * Adds a listener that listens on when this specific status is obtained or
	 * lost. If the user has been previously added, the old status should be
	 * overridden and the listener should listen on the new status
	 * 
	 * @param listener The listener that will observe
	 * 
	 * @param status   The status the listener will listen to
	 * 
	 * @throws IllegalArgumentException If the status is not valid
	 */
	public void addListener(StatusListener listener, String status) {
		checkIsValidStatus(status);
		this.statusListeners.put(listener, status);

	}

	/**
	 * Remove the listener
	 * 
	 * @param listener The listener to remove
	 */
	public void removeListener(StatusListener listener) {
		this.statusListeners.remove(listener);

	}

	/**
	 * Updates all listeners that were interested in either the old or the new
	 * status that the status of the user has changed. Observers should only be
	 * notified if oldStatus and newStatus is different
	 * 
	 * @param oldStatus The old status of the user
	 * 
	 * @param newStatus The new status of the user
	 */
	private void fireStatusChanged(String oldStatus, String newStatus) {
		for (StatusListener listener: this.statusListeners.keySet()) {
			String status = this.statusListeners.get(listener);
			if (oldStatus.equals(status) || newStatus.equals(status)) {
				listener.statusChanged(this.username, oldStatus, newStatus);
			}
		}
	}
}


Oppgave b) Implementer RentalCarListener. RentalCarListener lytter på endringer i status til LoyaltyUser i LoyaltyUser for å kunne gi gullmedlemmer rabatt.

  • statusChanged(String username, String oldStatus, String newStatus) - Registerer endringen i status for et gitt brukernavn.
  • getDiscount(String username) Returnerer rabatten til et brukernavn. Skal være 100 hvis brukeren innehar gullnivå, ellers 0.
Code Block
languagejava
import java.util.HashMap;
import java.util.Map;

/* RentalCar listeners listens to changes in Status for all userNames.*/
public class RentalCarListener implements StatusListener {

	// TODO - Add any needed fields here
	// Many will probably have solved this using a list instead, which is also a good solution
	private Map<String, Integer> rebates = new HashMap<>();
	private final String GOLD_STATUS = "Gold";
	@Override
	/**
	 * Method that should be called when a given userName has updated its status.
	 */
	public void statusChanged(String username, String oldStatus, String newStatus) {
		if (newStatus.equals(GOLD_STATUS)) {
			rebates.put(username, 100);
		}
		else {
			rebates.remove(username);
		}
	}

	/**
	 * Get's the discount of a user. Should be a 100 if the user currently has Gold
	 * status, otherwise should be 0.
	 * 
	 * @param username The username of the user
	 * 
	 * @return The discount the user qualifies for.
	 */
	public int getDiscount(String username) {
		return rebates.getOrDefault(username, 0);

	}
}



Expand
titleDel 6 - Arv og Debugging (10 %)

Ta utgangspunkt i den abstrakte klassen LoyaltyAward og klassen CarRentalAwards. Disse klassene implementerer metoder for å tildele poeng til en bruker ut ifra hvilket bilmerke den har leid.

Under implementasjonen av disse klassene har det kommet inn to feil som eksponeres ved kjøring av main-metoden i CarRentalAwards.

Rett opp feilene (dette vil innebære både at koden ikke krasjer og at riktig logikk utføres).

Følgende metoder finnes i LoyaltyAward:

  • LoyaltyAward(String awardName) - Oppretter et LoyaltyAward objekt med det gitte navnet.
  • setAwardName(String awardName) - Oppdaterer navnet på prisen.
  • awardPoints(int points, LoyaltyUser loyaltyUser) Gir antall poeng til den gitte brukeren.

Følgende metoder finnes i CarRentalAwards:

  • CarRentalAwards(String awardName Oppretter et CarRentalAwards objekt med det gitte navnet.
  • setAwardName(String awardName) - Oppdaterer navnet på prisen dersom det er gyldig.
  • awardPoints(int carBrand, LoyaltyUser loyaltyUser) - Gir et visst antall poeng til den gitte brukeren. Antall poeng er beregnet ut ifra hvilket bilmerke brukeren har leid.
Expand
titleKodeskjelett del 6


Code Block
languagejava
public abstract class LoyaltyAward {

	private String awardName;

	public LoyaltyAward(String awardName) {
		this.setAwardName(awardName);
	}

	/**
	 * Updates the name of the award
	 * @param awardName the new award name
	 */
	public void setAwardName(String awardName) {
		this.awardName = awardName;
	}

	/**
	 * Adds the points to the given loyalty user.
	 *
	 * @param points      The points to award to the user
	 * @param loyaltyUser The user that rented the car
	 */

	public void awardPoints(int points, LoyaltyUser loyaltyUser) {
		loyaltyUser.addPoints(points);
	}
}


import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class CarRentalAwards extends LoyaltyAward {

	private Map<Integer, Integer> carBrandToPoints = Map.of(1, 1, 2, 10, 3, 100, 4, 200, 5, 500);
	private List<String> validNames = Arrays.asList("CarRentalAgency1", "CarRentalAgency2");

	public CarRentalAwards(String awardName) {
		super(awardName);
	}

	@Override
	/**
	 * Updates the award name
	 * @param: awardName The name of the award
	 * 
	 * @throws IllegalArgumentException If the award name is not part of the valid
	 *                                  names
	 */
	public void setAwardName(String awardName) {
		if (!validNames.contains(awardName)) {
			throw new IllegalArgumentException("Invalid award name");
		}
		super.setAwardName(awardName);
	}

	/**
	 * Updates the status of the given LoyaltyUser with points based on the map
	 * above. The map means that carBrand 1 will award 1 points, carBrand 2 will
	 * award 10 points etc
	 * 
	 * @param carBrand:    The brand of the car the user has rented. If the brand
	 *                     does not exist 0 points should be awarded
	 * 
	 * @param loyaltyUser: The user that rented the car
	 * 
	 * 
	 */
	public void awardPoints(int carBrand, LoyaltyUser loyaltyUser) {
		Integer points = carBrandToPoints.get(carBrand);
		if (points != null) {
			awardPoints(points, loyaltyUser);
		}
	}

	public static void main(String[] args) {
		LoyaltyUser user = new LoyaltyUser("Name");
		// What goes wrong here
		LoyaltyAward award = new CarRentalAwards("CarRentalAgency1");
		// What goes wrong here
		award.awardPoints(1, user);
		System.out.println(user.getPoints());
	}

}



Expand
titleLF

Mangler



Expand
titleDel 7 - Filhåndtering (15 %)

Ta utgangspunkt i den vedlagte Course-klassem. Denne klassen trengs ikke å endres for oppgaven. Course-klassen har oversikt over:

  • Et navn.
  • Gjennomsnittskarakter.
  • Andre emner som er forkunnskapskrav til dette emnet.

Du skal fylle ut UniversityHandbook -klassen sine metoder for å kunne lese emner fra fil og holde oversikt over disse emenene. Det vil bli gitt poeng etter følgende oppnåelse, så dersom du ikke får til hele oppgaven vil du få uttelling for delvis løsning.

  1. Lese inn emners navn og gjennomsnittskarakter fra fil.
  2. Støtte å legge til forkunnskapskrav om emner som har vært tidligere i filen.
  3. Støtte å legge til forkunnskapskrav om emner som kommer senere i filen.

Følgende metoder skal fullføres/implementeres:

  • readFromInputStream(InputStream stream) - Leser inn emner fra en gitt inputStream. (Se eksempelfilen i src/main/resources/del7_og_8/courses.txt for hvordan disse kan se ut.)
  • getCourse(String courseName) - Returnerer emnet med det gitte navnet.
Expand
titleKodeskjelett del 7


Code Block
languagejava
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class Course {
	// The name of the course
	private String courseName;
	// A list of course prerequisites for this course. This means that all the courses in this list should be taken before this one. 
	private List<Course> prerequisites = new ArrayList<>();
	// The average grade of the course
	private double averageGrade;

	public Course(String courseName, double averageGrade) {
		this(courseName);
		this.averageGrade = averageGrade;
	}

	public Course(String courseName) {
		this.courseName = courseName;
	}
	
	public String getCourseName() {
		return courseName;
	}

	public void setAverageGrade(double averageGrade) {
		this.averageGrade = averageGrade;
	}
	
	public double getAverageGrade() {
		return averageGrade;
	}
	
	/**
	 * Adds a new prerequisite to this course
	 * @param course The course to add
	 */
	public void addPrequisite(Course course) {
		this.prerequisites.add(course);
	}
	
	/**
	 * 
	 * @return A copy of the list of prerequisites
	 */
	public Collection<Course> getPrerequisites() {
		return new ArrayList<>(prerequisites);
	}
	
	public String toString() {
		return this.courseName;
	}

}


import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class UniversityHandbook {

	private List<Course> courses = new ArrayList<>();

	/**
	 * Reads all the courses from a given input stream. The courses are on this
	 * form: courseName, averageGrade, prerequisite 1, prerequisite 2,
	 * prerequisite 3....
	 * 
	 * See courses.txt in src/main/resources/del7_og_8 for an example file.
	 * 
	 * Calling this method should remove any existing courses from the handbook.
	 * 
	 * A given course can have anything from 0 to unlimited number of prerequisites.
	 * The courses do not necessary come in order. Meaning that a course may appear
	 * in the prerequisite list as a never before seen course. The method should read
	 * in all courses, and set the courseName, averageGrade and prerequisites of all
	 * courses and add the courses to the courses field of this class.
	 * 
	 * A skeleton code to read from file is provided to you but feel free to write
	 * your own code for this.
	 * 
	 * You can assume that all lines from the file will be on the correct format.
	 * 
	 * @param stream InputStream containing the course data
	 */
	public void readFromInputStream(InputStream stream) {
		try (Scanner scanner = new Scanner(stream)) {
			while (scanner.hasNextLine()) {
				String line = scanner.nextLine();
				String[] details = line.split(",");
				// TODO - Continue implementation here
			}
		}
	}

	/**
	 * Gets the course with the courseName
	 * 
	 * @param courseName The name of the course
	 * 
	 * @return The course with the given name
	 */
	public Course getCourse(String courseName) {
		...
	}
}


Expand
titleLF


Code Block
languagejava
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Scanner;

public class UniversityHandbook {

	private Collection<Course> courses = new ArrayList<>();

	/**
	 * Reads all the courses from a given input stream. The courses are on this
	 * form: courseName, averageGrade, prerequisite 1, prerequisite 2,
	 * prerequisite 3....
	 * 
	 * See courses.txt in src/main/resources/del7_og_8 for an example file.
	 * 
	 * Calling this method should remove any existing courses from the handbook.
	 * 
	 * A given course can have anything from 0 to unlimited number of prerequisites.
	 * The courses do not necessary come in order. Meaning that a course may appear
	 * in the prerequisite list as a never before seen course. The method should read
	 * in all courses, and set the courseName, averageGrade and prerequisites of all
	 * courses and add the courses to the courses field of this class.
	 * 
	 * A skeleton code to read from file is provided to you but feel free to write
	 * your own code for this.
	 * 
	 * You can assume that all lines from the file will be on the correct format.
	 * 
	 * @param stream InputStream containing the course data
	 */
	public void readFromInputStream(InputStream stream) {
		this.courses = new ArrayList<>();
		try (Scanner scanner = new Scanner(stream)) {
			while (scanner.hasNextLine()) {
				String line = scanner.nextLine();
				String[] details = line.split(",");
				String courseName = details[0];
				double averageGrade = Double.parseDouble(details[1]);
				Course course = this.findOrCreateCourse(courseName);
				course.setAverageGrade(averageGrade);
				
				for (int i=2;i<details.length; i++) {
					String prequesite = details[i];
					Course coursePrequesite = findOrCreateCourse(prequesite);
					course.addPrequisite(coursePrequesite);
				}
				
			}
		}
	}


	/**
	 * Get's the course with the courseName
	 * 
	 * @param courseName The name of the course
	 * 
	 * @return The course with the given name
	 */
	public Course getCourse(String courseName) {
		for(Course course: courses) {
			if (course.getCourseName().equals(courseName)) {
				return course;
			}
		}
		return null;
	}

	private Course findOrCreateCourse(String name) {
		Course course = getCourse(name);
		if (course != null) {
			return course;
		}
		course = new Course(name);
		this.courses.add(course);
		return course;
	}
}





Del 8 - Funksjonelle grensesnitt og lister (10 %)
Expand
titleDel 8 - Funksjonelle grensesnitt og lister (10 %)

Fyll ut UniversityHandbookUtils sine metoder for operasjoner på en liste med Course-objekter.

Følgende metoder skal implementeres:

  • getCoursesWithPredicate(Collection courses, Predicate p) - Returnerer alle emner i samlingen som tilfredstiller predikatet.
  • getNonPrequisiteCourses(Collection courses) - Returnerer alle emner i samlingen som ikke har noen forkunnskapskrav.
  • containsImpossibleCourse(Collection courses) - Returnerer hvorvidt samlingen av emner inneholder et emne som er umulig. Et umulig emne er definert som et emne X som har et forkunnskaps krav, som har emnet X som forkunnskapskrav.

    Expand
    titleKodeskjelett del 8


    Code Block
    languagejava
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collection;
    import java.util.List;
    import java.util.function.Predicate;
    
    public class UniversityHandbookUtils {
    
    	/**
    	 * Get all courses that matches the given predicate
    	 * 
    	 * @param courses The list of courses to check for
    	 * @param p       The predicate that should be matched on
    	 *
    	 * @return A collection of courses that satisfy the predicate.
    	 */
    	public static Collection<Course> getCoursesWithPredicate(Collection<Course> courses, Predicate<Course> p) {
    		...
    	}
    
    	/**
    	 * Get all courses that does not have any prerequisites
    	 * 
    	 * @param courses The list of courses to check for
    	 * @return A collection of course without any prerequisites
    	 */
    	public static Collection<Course> getNonPrequisiteCourses(Collection<Course> courses) {
    		...
    
    	}
    	
    	/**
    	 * Returns whether the handbook contains an impossible course. A course is
    	 * deemed impossible if any of the prerequisite of the course has the current
    	 * course as a prerequisite. Only direct dependencies need to be checked. You
    	 * do not need to worry about transitive dependencies. That means if TDT4100 has
    	 * a dependency on TDT4110 and TDT4110 has a dependency on TDT4100 it is impossible.
    	 * 
    	 * A transitive dependency that does not need to be checked is if TDT4100 has a
    	 * dependency on TDT4110, TDT4110 has dependency on TDT4200 and TDT4200 has a
    	 * dependency on TDT4100.
    	 * 
    	 * @param courses The list of courses to check for
    	 * @return whether the courses contains an impossible course
    	 */
    	public static boolean containsImpossibleCourse(Collection<Course> courses) {
    		...
    	}
    }



    Expand
    titleLF


    Code Block
    languagejava
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collection;
    import java.util.List;
    import java.util.function.Predicate;
    import java.util.stream.Collectors;
    
    public class UniversityHandbookUtils {
    
    	/**
    	 * Get all courses that matches the given predicate
    	 * 
    	 * @param courses The list of courses to check for
    	 * @param p       The predicate that should be matched on
    	 *
    	 * @return A collection of courses that satisfy the predicate.
    	 */
    	public static Collection<Course> getCoursesWithPredicate(Collection<Course> courses, Predicate<Course> p) {
    		return courses.stream().filter(p).collect(Collectors.toList());
    
    	}
    
    	/**
    	 * Get all courses that does not have any prerequisites
    	 * 
    	 * @param courses The list of courses to check for
    	 * @return A collection of course without any prerequisites
    	 */
    	public static Collection<Course> getNonPrequisiteCourses(Collection<Course> courses) {
    		return courses.stream().filter(course -> course.getPrerequisites().size() == 0).collect(Collectors.toList());
    	}
    	
    	/**
    	 * Returns whether the handbook contains an impossible course. A course is
    	 * deemed impossible if any of the prerequisite of the course has the current
    	 * course as a prerequisite. Only direct dependencies need to be checked. You
    	 * do not need to worry about transitive dependencies. That means if TDT4100 has
    	 * a dependency on TDT4110 and TDT4110 has a dependency on TDT4100 it is impossible.
    	 * 
    	 * A transitive dependency that does not need to be checked is if TDT4100 has a
    	 * dependency on TDT4110, TDT4110 has dependency on TDT4200 and TDT4200 has a
    	 * dependency on TDT4100.
    	 * 
    	 * @param courses The list of courses to check for
    	 * @return whether the courses contains an impossible course
    	 */
    	public static boolean containsImpossibleCourse(Collection<Course> courses) {
    		return courses.stream().anyMatch(course -> course.getPrerequisites().stream().anyMatch(prerequesite -> prerequesite.getPrerequisites().contains(course)));
    
    	}
    }
Expand
titleDel 6 - Arv og Debugging (10 %)
Expand
titleDel 7 - Filhåndtering (15 %)
Expand
title