1.6 Musterklassenarbeit

Papierversion, ausdrucken und ohne weitere Hilfsmittel versuchen zu lösen.

Klassenarbeitsverwaltung für Schüler

Hinweis: Quellcode muss sauber formatiert und sinnvoll kommentiert sein!

Klassendiagramm Vorgabe
Klassendiagramm Vorgabe

1. Ausgeben aller Klassenarbeiten 10P

Erstellen Sie ein Struktogramm für die Methode ausgebenKlassenarbeiten(), die für alle vorhandenen Klassenarbeiten deren Methode ausgeben() aufruft.

Lösung
Struktogramm

2. Methoden neueKlassenarbeit(..) 20P

Um die Noten neuer Klassenarbeiten zu speichern sind in der Klasse Schueler zwei Methoden vorgesehen. Es muss immer ein Fach angegeben werden. Erstellen Sie den Quelltext für:
2.1 neueKlassenarbeit(pFach:Text, pNote:FKZ) überprüft die Noteneingabe. Ausgabe im Fehlerfall: „Fehlerhafte Note“

Lösung
public void neueKlassenarbeit(String pFach, double pNote){
  if (pFach==null || pFach=="" || pNote<1.0 || pNote>6.0){
    System.out.println("Fehlerhafte Note!");  
  }else
    dieKlassenarbeit.add(new Klassenarbeit(pFach,pNote));
  }
}
import java.io.*;
public class Klassenarbeit{
  private String aFach;  //{nicht leer}
  private double aNote;  //{>=1.0;<=6.0}
  public Klassenarbeit(String pFach, double pNote){
    if (pFach==null || pFach=="" || pNote<1.0 || pNote>6.0){
      aFach="Fehlerhafte Note";
      System.out.println(aFach);
    }
    else{
      aFach=pFach;
      aNote=pNote;
    }
  }
  public String getFach(){
    return aFach;  
  }
  public double getNote(){
    return aNote;
  }
  public void ausgeben(){
    System.out.printf("%-8s %2.1f\n",aFach,aNote);  
  }
  public String rueckgebenDatensatz(){
    return String.format("%s;%2.1f\n",aFach,aNote);  
  }
}

2.2 neueKlassenarbeit(pFach:Text, pMax:GZ, pPunkte:GZ) überprüft die Punkteingabe (0<=pPunkte<=pMax). Ausgabe im Fehlerfall: „Fehlerhafte Daten“.
Die Note soll auf eine Nachkommastelle gerundet gespeichert werden.
Tipp: note = 6-5*pPunkte/pMax

Lösung
public void neueKlassenarbeit(String pFach, int pMax, int pPunkte){
  if (pFach==null || pFach=="" || pMax<=0 || pPunkte<0 || pPunkte > pMax){
    System.out.println("Fehlerhafte Daten");
  }else {
    double n = ((int)((6-(5.0*pPunkte/pMax))*10+0.5))/10.0;
    dieKlassenarbeit.add(new Klassenarbeit(pFach,n));
  }
}

2.3 Um welche Art von Polymorphie handelt es sich dabei? Begründen Sie
und gehen dabei auf den Begriff Signatur ein.

Lösung

Es handelt sich um statische Polymorphie, Überladen von Operationen. Bereits zur Compilezeit kann entschieden werden welche der beiden Methoden verwendet werden sollen. Die Signatur ist der Name der Methode und die Liste der Parameterdatentypen, hier:
neueKlassenarbeit(Text,FKZ), neueKlassenarbeit(Text,GZ,GZ).

3. Datensätze aus Datei einlesen 10P

Die Datensätze werden mit der Methode Klassenarbeit.rueckgebenDatensatz() in einer CSV-Datei gespeichert.
Erstellen Sie den Quellcode für die Methode leseCSV(pFilename:Text) die die Datensätze aus einer CSV-Datei einliest und hinzu fügt.

Lösung
public void leseCSV(String pFilename)  { // try-catch nicht im Struktogramm darstellbar
  String zeile;
  String[] attribute;
  try {  // versuchen wir mal folgenden Code aus zu führen
    BufferedReader ein = new BufferedReader(new FileReader(pFilename));
    while ((zeile = ein.readLine())!= null){ // solange Datensätze vorhanden
      zeile=zeile.replace(',','.'); // damit Doubleparsen geht
      attribute = zeile.split(";"); // zerlege
      neueKlassenarbeit(attribute[0],Double.parseDouble(attribute[1]));
    }  
    ein.close();
  } catch (IOException e) {  // falls ein IO-Ausnahmefehler auftritt tue folgendes
    System.out.println("IOException: " + e);  // gib den Fehler auf der Konsole aus   
  }
}

4. Objektdiagramm zur Testmethode 10P

In der Klasse Schueler werden in einer Methode test() nebenstehende Programmzeilen ausgeführt. Erstellen Sie ein Objektdiagramm.

Lösung
Objektdiagramm
neueKlassenarbeit("SAE",3.0);
neueKlassenarbeit("Deutsch",2);
neueKlassenarbeit("BWL",30,20);

5. Erweitern und modifizieren des Klassendiagramms 10P

Als Fachnoten gibt es nicht nur Klassenarbeiten sondern auch mündliche Noten.
Erweitern und modifizieren Sie das Klassendiagramm: Es gibt eine Oberklasse Noten und zwei Unterklassen Klassenarbeit und Muendlich.

Lösung
Klassendiagramm

Zur Übung alles implementieren

Implementieren Sie die Klassen Schuler und Klassenarbeiten. Test-Methode:

  public void test(){
    System.out.println("\f");
    neueKlassenarbeit("SAE",3.0);
    neueKlassenarbeit("Deutsch",2);
    neueKlassenarbeit("BWL",30,20);
    neueKlassenarbeit("SAE",4);
    neueKlassenarbeit("ITS",2.4);
    ausgebenKlassenarbeiten();
    ausgebenDurchschnitt("Deutsch");
    ausgebenDurchschnitt("SAE");
    ausgebenDurchschnitt("Englisch");
    schreibeCSV("noten.txt");
    leseCSV("noten.txt");
    ausgebenKlassenarbeiten();
  }
Lösung Schueler
import java.io.*;
import java.util.ArrayList;

public class Schueler{
  private ArrayList<Klassenarbeit> dieKlassenarbeit = new ArrayList<Klassenarbeit>();
  
  public void neueKlassenarbeit(String pFach, int pMax, int pPunkte){
    if (pFach=="" || pMax<=0 || pPunkte<0 || pPunkte > pMax){
      System.out.println("Fehlerhafte Daten");
    }else {
      double n = ((int)((6-(5.0*pPunkte/pMax))*10+0.5))/10.0;
      dieKlassenarbeit.add(new Klassenarbeit(pFach,n));
    }
  }
  public void neueKlassenarbeit(String pFach, double pNote){
    if (pFach=="" || pNote<1.0 || pNote>6.0){
      System.out.println("Fehlerhafte Note!");  
    }else
      dieKlassenarbeit.add(new Klassenarbeit(pFach,pNote));
  }
  public void ausgebenKlassenarbeiten(){
    int i=0;
    while (i<dieKlassenarbeit.size()){
      dieKlassenarbeit.get(i++).ausgeben();
    }
  }
  public double durchschnitt(String pFach){
    double sum=0;
    int n=0;
    int i=0;
    while (i<dieKlassenarbeit.size()){
      if(dieKlassenarbeit.get(i).getFach()==pFach){
        sum += dieKlassenarbeit.get(i).getNote();
        n++;
      }
      i++;
    }
    if (n>0)
      return sum /n;
    System.out.println("Keine "+pFach+"-Note vorhanden!");
    return 0.0;
  }
  public void ausgebenDurchschnitt(String pFach){
    System.out.printf("Durchschnitt im Fach %8s: %2.1f\n",pFach,durchschnitt(pFach));
  }
  public void schreibeCSV(String pFilename)  { // try-catch nicht im Struktogramm darstellbar
    int i=0;
    try {  // versuchen wir mal folgenden Code aus zu führen
      BufferedWriter aus = new BufferedWriter(new FileWriter(pFilename));
      while (i<dieKlassenarbeit.size()){
        aus.write(dieKlassenarbeit.get(i).rueckgebenDatensatz());
        i++;
    }  
    aus.close();
    } catch (IOException e) {  // falls ein IO-Ausnahmefehler auftritt tue folgendes
     System.out.println("IOException: " + e);  // gib den Fehler auf der Konsole aus   
    }
  }
  public void leseCSV(String pFilename)  { // try-catch nicht im Struktogramm darstellbar
    String zeile;
    String[] attribute;
    try {  // versuchen wir mal folgenden Code aus zu führen
      BufferedReader ein = new BufferedReader(new FileReader(pFilename));
      while ((zeile = ein.readLine())!= null){ // solange Datensätze vorhanden
        zeile=zeile.replace(',','.'); // damit Doubleparsen geht
        attribute = zeile.split(";"); // zerlege
        neueKlassenarbeit(attribute[0],Double.parseDouble(attribute[1]));
    }  
    ein.close();
    } catch (IOException e) {  // falls ein IO-Ausnahmefehler auftritt tue folgendes
     System.out.println("IOException: " + e);  // gib den Fehler auf der Konsole aus   
    }
  }
  public void test(){
    System.out.println("\f");
    neueKlassenarbeit("SAE",3.0);
    neueKlassenarbeit("Deutsch",2);
    neueKlassenarbeit("BWL",30,20);
    neueKlassenarbeit("SAE",4);
    neueKlassenarbeit("ITS",2.4);
    ausgebenKlassenarbeiten();
    ausgebenDurchschnitt("Deutsch");
    ausgebenDurchschnitt("SAE");
    ausgebenDurchschnitt("Englisch");
    schreibeCSV("noten.txt");
    leseCSV("noten.txt");
    ausgebenKlassenarbeiten();
  }
}

Implementieren Sie die Klassen nach der Modifizierung des Klassendiagramms: Schueler, Note, Klassenarbeit und Muendlich. Testmethode:

Hinweis zu „noten.txt“: Sollte eigentlich „noten.csv“ genannt werden, aber netterweise zeigt BlueJ txt-Dateien im ProjektBrowser an…

Ausgabe in der CSV-Datei der Testmethode:

SAE;3,0;K
Deutsch;2,0;K
BWL;2,7;K
SAE;4,0;M
ITS;2,4;K
  public void test(){
    System.out.println("\f");
    neueNote('K',"SAE",3.0);
    neueNote('K',"Deutsch",2);
    neueNote("BWL",30,20);
    neueNote('M',"SAE",4);
    neueNote('K',"ITS",2.4);
    ausgebenNoten();
    ausgebenDurchschnitt("Deutsch");
    ausgebenDurchschnitt("SAE");
    ausgebenDurchschnitt("Englisch");
    schreibeCSV("noten.txt");
    leseCSV("noten.txt");
    ausgebenNoten();
  }
Lösung Note
import java.io.*;
public abstract class Note{
  private String aFach;  //{nicht leer}
  private double aNote;  //{>=1.0;<=6.0}
  public Note(String pFach, double pNote){
    if (pFach==null || pFach=="" || pNote<1.0 || pNote>6.0){
      aFach="Fehlerhafte Note";
      System.out.println(aFach);
    }
    else{
      aFach=pFach;
      aNote=pNote;
    }
  }
  public String getFach(){
    return aFach;  
  }
  public double getNote(){
    return aNote;
  }
  public abstract void ausgeben();
  
  public abstract String rueckgebenDatensatz();
}
Lösung Klassenarbeit
import java.io.*;
public class Klassenarbeit extends Note{
  
  public Klassenarbeit(String pFach, double pNote){
    super(pFach,pNote);
  }
  
  public void ausgeben(){
    System.out.printf("%-8s %2.1f Klassenarbeit\n",getFach(),getNote());  
  }
  public String rueckgebenDatensatz(){
    return String.format("%s;%2.1f;K\n",getFach(),getNote());  
  }
}
Lösung Schueler
import java.io.*;
import java.util.ArrayList;

public class Schueler{
  private ArrayList<Note> dieNote = new ArrayList<Note>();
  public void neueNote(char pArt,String pFach, double pNote){
    if (pFach==null || pFach=="" || pNote<1.0 || pNote>6.0){
      System.out.println("Fehlerhafte Note!");  
    }else{
      switch (pArt){
        case 'k':
        case 'K':
          dieNote.add(new Klassenarbeit(pFach,pNote));
          break;
        case 'm':
        case 'M':
          dieNote.add(new Muendlich(pFach,pNote));
          break;
        default:
          System.out.println("Unbekannte Art!");    
      }
    }
  }
  public void neueNote(String pFach, int pMax, int pPunkte){
    if (pFach==null || pFach=="" || pMax<=0 || pPunkte<0 || pPunkte > pMax){
      System.out.println("Fehlerhafte Daten");
    }else {
      double n = ((int)((6-(5.0*pPunkte/pMax))*10+0.5))/10.0;
      dieNote.add(new Klassenarbeit(pFach,n));
    }
  }
  public void ausgebenNoten(){
    int i=0;
    while (i<dieNote.size()){
      dieNote.get(i++).ausgeben();
    }
  }
  public double durchschnitt(String pFach){
    double sum=0;
    int n=0;
    int i=0;
    while (i<dieNote.size()){
      if(dieNote.get(i).getFach()==pFach){
        sum += dieNote.get(i).getNote();
        n++;
      }
      i++;
    }
    if (n>0)
      return sum /n;
    System.out.println("Keine "+pFach+"-Note vorhanden!");
    return 0.0;
  }
  public void ausgebenDurchschnitt(String pFach){
    System.out.printf("Durchschnitt im Fach %8s: %2.1f\n",pFach,durchschnitt(pFach));
  }
  public void schreibeCSV(String pFilename)  { // try-catch nicht im Struktogramm darstellbar
    int i=0;
    try {  // versuchen wir mal folgenden Code aus zu führen
      BufferedWriter aus = new BufferedWriter(new FileWriter(pFilename));
      while (i<dieNote.size()){
        aus.write(dieNote.get(i).rueckgebenDatensatz());
        i++;
    }  
    aus.close();
    } catch (IOException e) {  // falls ein IO-Ausnahmefehler auftritt tue folgendes
     System.out.println("IOException: " + e);  // gib den Fehler auf der Konsole aus   
    }
  }
  public void leseCSV(String pFilename)  { // try-catch nicht im Struktogramm darstellbar
    String zeile;
    String[] attribute;
    try {  // versuchen wir mal folgenden Code aus zu führen
      BufferedReader ein = new BufferedReader(new FileReader(pFilename));
      while ((zeile = ein.readLine())!= null){ // solange Datensätze vorhanden
        zeile=zeile.replace(',','.'); // damit Doubleparsen geht
        attribute = zeile.split(";"); // zerlege
        neueNote(attribute[2].charAt(0),attribute[0],Double.parseDouble(attribute[1]));
    }  
    ein.close();
    } catch (IOException e) {  // falls ein IO-Ausnahmefehler auftritt tue folgendes
     System.out.println("IOException: " + e);  // gib den Fehler auf der Konsole aus   
    }
  }
  public void test(){
    System.out.println("\f");
    neueNote('K',"SAE",3.0);
    neueNote('K',"Deutsch",2);
    neueNote("BWL",30,20);
    neueNote('M',"SAE",4);
    neueNote('K',"ITS",2.4);
    ausgebenNoten();
    ausgebenDurchschnitt("Deutsch");
    ausgebenDurchschnitt("SAE");
    ausgebenDurchschnitt("Englisch");
    schreibeCSV("noten.txt");
    leseCSV("noten.txt");
    ausgebenNoten();
  }
}

Verletzung der OOP Designrichtlinien?

  1. Informationsexperte
    Wer ist der Informationsexperte für diese Aufgabe? D.h. wer hat bzw. sollte die Information für diese Aktion haben, wer muss also angefragt werden?
  2. Geringe Kopplung
    Eine Klasse sollte möglichst wenig Verbindungen zu anderen Klassen aufweisen, so sind bei Änderungen weniger Klassen betroffen.
  3. Hoher Zusammenhalt
    Die Verantwortlichkeit einer Klasse sollte sich möglichst nur auf eine logische Aufgabe beziehen.
  4. Erzeuger
    Die Klasse ist für die Erzeugung neuer Objekte zuständig, welche die zu erzeugenden Objekte beinhaltet (Komposition, Aggregation), welche die Informationen zum Start des neuen Objekts enthält oder das zu erzeugende Objekt eng verwendet.

Die Zusicherungen werden in der Klasse Schueler und Klassenarbeit abgefragt. Ausserdem lassen sich Klassenarbeiten mit ungültigen Werten erzeugen.
Welche Designrichtline(n) wurde(n) verletzt? Nehmen Sie dazu Stellung.

Reparieren Sie das Design durch Zuhilfenahme von Exceptions.