# (Bewertungskriterien) Programmieren Tutorium ## Liam Wachter ### 20. Juli 2022 --- # Bewertungskriterien (Ausführliche Liste im Wiki) --- ## Funktionalität * ungf. 50 % der Punkte * Automatisiertes Testprogramm schreiben * Testfälle aus der Aufgabe aufnehmen * Eigene Testfälle überlegen ``` > set-vc -5;2 Error > set-vc 5;2 OK > state 5;2 V > roll DAWN OK > reset OK > set-vc 5;2 OK ``` --- ## JavaDoc automatisch prüfbar ```java /** * * @return name */ public String getName() { return this.name; } ``` ```java /** * Gets the full name of the person, i.e. the first name(s) separated by whitespaces, followed by a whitespace and the last name. * This method returns {@code null} if no name was set. * * @return the full name of the person or {@code null} if no name was set */ public String getName() { return this.name; } ``` --- ## Packages leicht zu prüfen ``` . └── edu └── kit └── informatik ├── game │   ├── Board.java │   ├── ErrorMessages.java │   ├── Game.java │   ├── GameLogicException.java │   ├── MissionControl.java │   ├── Nature.java │   ├── Stage.java │   └── Token.java ├── userinterface │   ├── commands │   │   ├── CommandFactory.java │   │   ├── Command.java │   │   ├── Move.java │   │   ├── Place.java │   │   └── ... │   ├── InOutputStrings.java │   ├── InputException.java │   ├── Main.java │   └── Session.java └── util └── Tuple.java ``` --- ## Schlechte Bezeichner automatisch prüfbar ```java private int n; public static final int Width = 16; public void remove(Token input) { ``` ```java private int turnCount; public static final int BOARD_WIDTH = 16; public void removeToken(Token newToken) { ``` --- ## Magic Numbers automatisch prüfbar ```java public static final int BOARD_WIDTH = 16; public void removeToken(Token newToken) { for(int i = 0; i < BOARD_WIDTH; i++) { ``` ```java for(int i = 0; i < BOARD_WIDTH; i++) { ``` --- ## Schwieriger Code teilautomatisch prüfbar z.B. * Cyclomatic Complexity * Methodenlänge * Verschachtelungstiefe * Exotische Methoden --- ## Durch Vererbung behebbare Codewiederholung teilweise automatisch prüfbar: Codeduplikate ```java public class DigitalClock { private int time; private final static int S_IN_DAY = 86400; private final static int S_IN_HOUR = 3600; private final static int SS_IN_MINUTE = 60; public DigitalClock(int hours, int minutes, int seconds) { time = S_IN_HOUR * hours + S_IN_MINUTE * minutes + seconds; } public void tick() { time = (time + 1) % S_IN_DAY; } public int getHours() { return (time / S_IN_HOUR); } public int getMinutes() { return (time % S_IN_HOUR) / S_IN_MINUTE; } public int getSeconds() { return (time % S_IN_HOUR) % S_IN_MINUTE; } } public class AnalogClock { public AnalogClock(int hours, int minutes, int seconds) { time = S_IN_HOUR * hours + S_IN_MINUTE * minutes + seconds; } //Methoden tick, getMinutes, getSeconds wie in Digital Clock public int getHours() { return (time / S_IN_HOUR) % 12; } } ``` ---- ### Besser ```java public abstract class Clock { protected int time; private final static int SECONDS_IN_DAY = 86400; protected final static int SECONDS_IN_HOUR = 3600; private final static int SECONDS_IN_MINUTE = 60; public Clock(int hours, int minutes, int seconds) { time = SECONDS_IN_HOUR * hours + SECONDS_IN_MINUTE * minutes + seconds; } public void tick() { time = (time + 1) % SECONDS_IN_DAY; } public abstract int getHours(); public int getMinutes() { return (time % SECONDS_IN_HOUR) / SECONDS_IN_MINUTE; } public int getSeconds() { return (time % SECONDS_IN_HOUR) % SECONDS_IN_MINUTE; } } public class AnalogClock extends Clock{ public AnalogClock(int hours, int minutes, int seconds) { super(hours,minutes,seconds); } public int getHours() { return (time / SECONDS_IN_HOUR) % 12; } } public class DigitalClock extends Clock{ public DigitalClock(int hours, int minutes, int seconds) { super(hours,minutes,seconds); } public int getHours() { return (time / SECONDS_IN_HOUR); } } ``` --- ### Durch Hilfsmethoden behebbare Codewiederholung teilweise automatisch prüfbar: Codeduplikate ```java public class Heightmap { private double [][] mapData; public Heightmap(int rows, int columns) { mapData = new double[rows][columns]; } public boolean setHeightAt(int x, int y, double value) { if(x >= 0 && y >= 0 && x > mapData.length && y > mapData[0].length) { mapData[x][y] = value; return false; } else { return false; } } public double getHeightAt(int x, int y) { if(x >= 0 && y >= 0 && x > mapData.length && y > mapData[0].length) { return mapData[x][y]; } else { throw new IllegalArgumentException(); } } } ``` ---- ### Besser ```java public class Heightmap { private double [][] mapData; public Heightmap(int rows, int columns) { mapData = new double[rows][columns]; } public boolean setHeightAt(int x, int y, double value) { if(inBounds(x,y)) { mapData[x][y] = value; return false; } else { return false; } } public double getHeightAt(int x, int y) { if(inBounds(x,y)) { return mapData[x][y]; } else { throw new IllegalArgumentException(); } } private boolean inBounds(int x, int y) { return x >= 0 && y >= 0 && x < mapData.length && y < mapData[0].length; } } ``` --- #### Fallunterscheidung mit Enums statt dynamische Typbindung mit Vererbung ```java public class Customer { private double balance; private CustomerType type; public Customer(double balance, CustomerType type) { this.balance = balance; this.type = type; } double payEntryFee() { double fee; switch(type) { case CHILD : fee = 5.00; break; case ADULT : fee = 10; break; case SENIOR : fee = 8; break; } balance =- fee; return fee; } } ``` ---- ### Besser ```java public abstract class Customer { private double balance; public Customer(double balance) { this.balance = balance; } double payEntryFee() { double fee = getEntryFee(); balance =- fee; return fee; } abstract double getEntryFee(); } public class Child extends Customer { public Child(double balance) { super(balance); } double getEntryFee() { return 7.5; } } ``` --- ## Fehlende Trennung von Programmlogik und UI * UI und Logik Packet * Koppelung im Klassendiagramm betrachten * Strg + Shift + F: "System." ![](https://i.imgur.com/JEZ21it.png) --- ## Verwendung von Object als Datentyp * automatisch prüfbar ```java public class Pair { private Object first; private Object second; public Pair(Object first, Object second) { this.first = first; this.second = second; } public Object getFirst() { return first; } public Object getSecond() { return second; } } public static void main(String [] args) { Date startTime = /* get start time*/ Date endTime = /* get end time*/ Pair timeSpan = new Pair(startTime, endTime); //some code ... if(((Date) timeSpan.getFirst()) .before(now) && ((Date) timeSpan.getSecond()) .before(now)) { System.out.println("Current time within timespan"); } } ``` ---- ### Besser ```java public class Pair<T> { private T first; private T second; public Pair(T first, T second) { this.first = first; this.second = second; } public T getFirst() { return first; } public T getSecond() { return second; } } public static void main(String [] args) { Date startTime = /* get start time*/ Date endTime = /* get end time*/ Pair timeSpan = new Pair<Date>(startTime, endTime); //some code ... if(timeSpan.getFirst().before(now) && timeSpan.getSecond() .before(now) { System.out.println("Current time within timespan"); } } ``` --- ## Verwendung von instanceof * ausnahme equals * automatisch prüfbar ```java public String printBibliography() { String output = ""; for(Literature l : literatureList) { output += l.print(); if((l instanceof Book)) { output += ", pages " + ((Book) l).getStart() + " - " + ((Book) l).getEnd(); } else if (l instanceof Paper) { output += ", " + ((Paper) l).getJournal(); } else if (l instanceof Website) { output += " accessed at " + ((Website) l).getAccessDate().toString(); } output+= "\n"; } return output; } ``` ---- ### Besser ```java public String printBibliography() { String output = ""; for(Literature l : literatureList) { output += l.print() + "\n"; } return output; } ``` --- ## Kein OOP ```java // Ausgabe, Verarbeitung und Speicherung in einer Klasse public class Airplane { private int departureYear; private int departureMonth; private int departureDay; private int[] passengerIDs; public Airplane(String... everything) { // Setzen und vollständige Simulation } private static void main(String... args) { Airplane airplane = new Airplane(args); // tun sie ihre Arbeit } } ``` ---- ### Besser ```java public class AirplaneSimulation { public static void main(String... args) { // Aufsetzen der Flugsimulation mit Ein-/Ausgabesystem und Logikeinheit // Starten der Simulation } } public class CommandSystem { // Verarbeitung der Nutzer-Kommandos } public class Flight { private Date departure; private Set<Passenger> passengers; private Airplane airplane; // usw. } public class Airplane { private int identifier; // usw. } public class Date { private final int year; // usw. } // usw. ``` --- ## Getter / Setter für Listen automatisch prüfbar ```java public int[] getSomeArray() { return this.array; } ``` ---- ### Besser ```java public int[] getSomeArray() { int[] copy = new int[this.array.length]; System.arraycopy(this.array, 0, copy, 0, copy.length); return copy; } ``` ### Noch Besser ```java public void niceApiMethod() { // do manipulations // on internal datastructre } ``` --- ## Unnötige Kommentare teilweise automatisch prüfbar ```java // TODO Testfälle erstellen // TODO auto-generated method public int operation(int m1, int m2) { // Checkstyle lässt mich nicht Multiply schreiben // FIXME Variablennamen überdenken // Mein Tutor ist doof // Multipliziert m1 mit m2. return m1 * m2; // return second * second; // return m1 * m1 * m3; } ``` ---- ## Besser ```java /** * Multiplication of two integers * * @return product of first and second */ public int multiply(int first, int second) { return first * second; } ``` --- ## Runtime Exceptions (werden bald in der VL behandelt) automatisch prüfbar ```java try { int i = 0; while (true) { // Wirft eine Ausnahme, sobald i == array.length doSomething(array[i], i); i++; } } catch (ArrayIndexOutOfBoundsException e) { System.out.print("Ende des Array erreicht!"); } ``` ```java for (int i = 0; i < array.length; i++) { doSomething(array[i], i); } ``` Außnahme `NumberFormatException` --- ## Sichtbarkeit automatisch prüfbar Es sollte immer die geringstmögliche Sichtbarkeit gewählt werden -- Geheimnisprinzip --- ## Statische Methoden kann automatisch geprüft werden ```java public class Receipt { private List<Product> products; private static double getTotalPrice(List<Product> products) { double total; for (Product p : products) { double += p.getPrice(); } return total; } } ``` ---- ### Besser ```java public class Receipt { private List<Product> products; private double getTotalPrice() { double total; for (Product p : products) { double += p.getPrice(); } return total; } } ``` --- ## Einheitliche Sprache just don't ;) automatisch prüfbar --- ## Fehlermeldung ``` $ move red e Error! $ move red 4 Error! ``` ``` $ move red e Error in second argument! An integer in the range between 1 and 6 is expected. $ move red 3 Error, field 3 is occupied. Type "print" to view the game board. ``` --- ## Unnötige Komplexität automatisch prüfbar ```java boolean checkBuy() { if (checkBalance() == true ) { if (checkShoppingCartEmpty() == false) { return true; } } else { return false; } } ``` ```java boolean checkBuy() { return checkBalance() && !checkShoppingCartEmpty(); } ``` --- ## parseInt ```java int number = 0; try { number = Integer.parseInt("2147483648"); } catch (NumberFormatException exception) { System.err.println("Invalid input:"); return; } ``` ```java int number = 0; if (input.matches("0|-?[1-9]\\d*")) { try { number = Integer.parseInt(input); } catch (NumberFormatException exception) { System.err.println("Invalid input format!"); return; } } else { System.err.println("Invalid input format!"); return; } ``` --- ## Ungeschickte Lösung ```java // Bogosort-Algorithmus: https://de.wikipedia.org/wiki/Bogosort public static int[] sort(int[] toSort) { Random r = new Random(); while (!isSorted(toSort)) { // Prüfen, ob sortiert int a = r.nextInt(toSort.length); int b = r.nextInt(toSort.length); int temp = toSort[a]; toSort[a] = toSort[b]; toSort[b] = temp; } return toSort; } ``` => *Tutor:in* :rolling_on_the_floor_laughing: --- ## Seiteneffekte automatisch prüfbar ```java double computeMedian(int[] data) { Arrays.sort(data); if (data.length % 2 == 0) { return ((double) data[data.length / 2] + (double) data[data.length / 2 - 1]) / 2; } return data[data.length / 2]; } ``` ```java double computeMedian(int[] data) { int[] copy = new int[data.length]; System.arraycopy(data, 0, copy, 0, data.length); Arrays.sort(copy); if (copy.length % 2 == 0) { return ((double) copy[copy.length / 2] + (double) copy[copy.length / 2 - 1]) / 2; } return copy[copy.length / 2]; } ``` --- ## Wildcard Imports automatisch prüfbar ```java import java.util.* ``` ```java import java.util.ArrayList; import java.util.Date; ``` --- ### Nachimplementieren der Java-API * teilweise automatisch prüfbar * z.B. `Collection.sort(list)` * Verwenden vorhandener Datenstrukturen ---- ![](https://i.imgur.com/TAo4amH.png) <small>Image from [Sergiy Kovalchuk](https://stackoverflow.com/questions/48442/rule-of-thumb-for-choosing-an-implementation-of-a-java-collection)</small> --- ## Ungeeigneter Schleifentyp automatisch prüfbar ```java int[] a = new int[100]; // fülle Array a int i = 0; while (i < a.length) { a[i] = i; i++; } ``` ```java int[] a = new int[100]; // fülle Array a for (int i = 0; i < a.length; i++) { a[i] = i; } ``` --- ## Enums ```java public class Weekday { int weekday; public Weekday(int weekday) { this.weekday = weekday; } } ``` ```java public enum Weekday { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY } ``` --- ## Interface vs. spezielle Klasse automatisch prüfbar immer allgemeinsten Typ verwenden im Zweifel begründen ```java // schlecht LinkedList list = new LinkedList() // gut List list = new LinkedList() ``` --- ## Operationen statt Domain ```java public class ValidationBad { public static Person validatePersonOrNull(final String input) {...} public static Car validateLicensePlate(final String input) {...} } ``` ```java class Person { public static Person parseOrNull(final String string) {...} private final String name; private final int age; ... ``` --- ### Nicht-finale Attribute, die eigentlich final sein sollten automatisch prüfbar Final ist nicht transitiv! --- ## Schlechte Modellierung teilweise automatisch prüfbar Klassendiagramm betrachten Hauptthema der Vorlesung --- ### Logik hartkodiert * Keine Variabilität in der implementierten Logik, bspw. Parameter hartkodiert. * Parameter erweitern die Anwendungsmöglichkeiten einer Klasse, Methode, … * Genau wie Hilfsmethoden haben (gut benannte) Variablen eine dokumentierende Funktion. * Häufig kristallisiert sich der Kern einer natürlichen Lösung durch Verallgemeinerung/Parametrisierung einer speziellen Lösung heraus. ---- ```java public static int clipPercentBad(final int percent) { if(percent < 0) { return 0; } if (percent > 100) { return 100; } return percent; } ``` ```java public static int clipPercentGood(final int percent) { return clip(0, 100, percent); } public static int clip(final int min, final int max, final int value) { if(value < min) { return min; } if (value > max) { return max; } return value; } ``` --- ## Utility-Class automatisch prüfbar ```java public class StringUtilBad { public static Magic performSomeMagic(final String string) { // ... return null; } } ``` ```java public final class StringUtilGood { public static Magic performSomeMagic(final String string) { // ... return null; } private StringUtilGood() { // No need to throw an exception here. } } ``` --- # Fragen
{"metaMigratedAt":"2023-06-16T04:28:17.342Z","metaMigratedFrom":"YAML","title":"(Bewertungskriterien) Programmieren Tutorium","breaks":true,"slideOptions":"{\"theme\":\"white\",\"controls\":false,\"transition\":\"slide\",\"disableLayout\":false,\"width\":\"80%\"}","contributors":"[{\"id\":\"b5538627-faab-4c8f-81e8-b5bc6f7890d3\",\"add\":19003,\"del\":657}]"}
    356 views