Übungsaufgaben zur TableView: Mitfahrgesuche als Tabelle und Durchlaufzeiten
https://bildung.social/@oerinformatik/
https://oer-informatik.de/javafx-tableview-buchungen-uebungsaufgaben
JavaFX, TableView, CellValueFactory, CellFactory, FilteredList, SortedList, Filter, Kanban, Durchlaufzeit, Median, Übungsaufgaben, Prüfungsvorbereitung
tl/dr; (Bearbeitungszeit insgesamt 57 min, mehrere Aufgaben): Übungen zur TableView in der JavaFX-Desktop-App: Du ordnest CellValueFactory, CellFactory, Filter und Sortierung zu und sagst vorher, was beim Klick auf einen Spaltenkopf passiert, baust die Mitfahrgesuche als Tabelle mit Zellen und Sortierung, ergänzt einen Filter mit Notizsuche samt Tests und wertest die Durchlaufzeiten eines Kanban-Boards aus [AI-Assisted]. (Zuletzt geändert am 11.11.2026)
Dieser Artikel ist Teil einer Artikelreihe über eine Desktop-App mit JavaFX: Übersicht / Kanban-Projektstart / Projekt anlegen / FXML, Layouts und CSS / Properties, Bindings und MVVM / Backend anbinden / TableView für Buchungen / Formulare und Dialoge / Anmeldung mit Keycloak / Verwaltung und Diagramme / GUI-Tests mit TestFX / Abnahmetest und Kanban-Metriken / Paketieren mit jpackage
Diese Übungsaufgaben gehören zum Artikel TableView für Buchungen: anzeigen, filtern, sortieren und bestätigen. Sie spielen im Projekt Mitfahrlabor und führen den Strang der Mitfahrgesuche weiter: Das Gesuche-Fenster für Ticket JFX-13 „Offene Gesuche an meinem Haltepunkt anzeigen“ lädt seit den Übungen zum Anbinden des Backends die Gesuche im Hintergrund, zeigt sie aber nur als Liste. Jetzt bekommt es eine Tabelle und einen besseren Filter.
Hinweise zur Bearbeitung: Bis zur Teil-1-Prüfung der IT-Berufe solltest Du die Basiskompetenz beherrschen, also Aufgabe 1.1. Als FIA, FIDP oder FIDV musst Du zur Teil-2-Prüfung Aufgaben auf Kernkompetenz-Ebene lösen können, das sind Aufgabe 2.1 und Aufgabe 2.2. Von FIA wird zusätzlich erwartet, dass ihnen Aufgaben auf Profilkompetenz-Ebene (Aufgabe 3.1) keine Probleme bereiten. Als Startcode für die Aufgaben 2.1 und 2.2 dient der Code-Stand src/05-backend-anbinden/uebung-mitfahrgesuche mit Deinem GesucheViewModel aus den letzten Übungen. Eine vollständige Beispiellösung mit allen Tests liegt unter src/06-tableview-buchungen/uebung-mitfahrgesuche; dort steht auch die Klasse FormatierteZelle aus dem Artikel. Code-Aufgaben löst Du am besten am Rechner und prüfst sie mit gradlew test (bzw. mvnw test). Die Operatoren (nennen, beschreiben, erläutern, beurteilen …) sind im Artikel Wie sind die Aufgaben aufgebaut? erklärt. Schreib Deine Lösung zuerst vollständig auf, bevor Du die Beispiellösung aufklappst.
Du möchtest eigene Aufgaben zum Üben von einem KI-Sprachmodell generieren lassen?
Kopiere den Text in ein KI-Sprachmodell Deiner Wahl und passe Kompetenzstufe, Anzahl, Bearbeitungszeit und Szenario bei Bedarf an. Wie die Aufgaben aufgebaut sind, beschreibt der Artikel Wie sind die Aufgaben aufgebaut?. Prüfe die Ergebnisse mit dem Lehrartikel: KI-Sprachmodelle machen Fehler.
Erstelle neue Übungsaufgaben zum Thema „TableView: Mitfahrgesuche als
Tabelle und Durchlaufzeiten“.
Bauplan: Halte Dich an Aufbau, Kompetenzstufen, Operatoren,
Anforderungsbereiche, Punkte und Zeitschätzung, die im Artikel
https://oer-informatik.de/aufgaben-aufbau beschrieben sind.
Fachliche Grundlage:
- https://oer-informatik.de/javafx-tableview-buchungen
- https://oer-informatik.de/szenario-mitfahrlabor
- https://oer-informatik.de/javafx-kanban-projektstart
- Stilbeispiel: https://oer-informatik.de/javafx-tableview-buchungen-uebungsaufgaben
Die Aufgaben sollen Inhalte zu diesen Schlagwörtern abdecken:
- JavaFX
- TableView
- CellValueFactory
- CellFactory
- FilteredList
- SortedList
- Filter
- Kanban
- Durchlaufzeit
- Median
Kompetenzstufe: Kernkompetenz (alternativ: Basiskompetenz, Profilkompetenz, Expertise)
Anzahl: 1 Aufgabe mit Teilaufgaben
Bearbeitungszeit in Minuten ca.: <5 bis 30 min>
Szenario: eines der Szenarien aus dem Bauplan
Vorgehen:
1. Lies zuerst die verlinkten Artikel. Kannst Du keine Webseiten
abrufen, sag mir das, statt zu raten.
2. Steht bei einer Angabe noch ein Platzhalter in <…>, frag mich
zuerst danach. Richte Punkte und Zahl der Teilaufgaben nach der
Bearbeitungszeit (ca. 0,9 min je Punkt).
3. Gib je Aufgabe Titel mit Kompetenzstufe, Ausgangssituation und
Teilaufgaben mit Operator und Punktzahl aus, dazu die geschätzte
Bearbeitungszeit.
4. Gib die Beispiellösungen mit Punkteverteilung erst aus, wenn ich
danach frage.
Aufgabe 1.1: Factorys, Filter und Sortierung zuordnen (Basiskompetenz)
(ca. 13 min Bearbeitungszeit)
Oksana Melnyk prüft JFX-05 in der Spalte „Test“ und möchte vorher wissen, ob Du die Bausteine der Tabelle auseinanderhalten kannst. Sie legt Dir sechs Wünsche und Codezeilen aus dem Gesuche-Fenster vor.
- Ordne jeder Zeile den Baustein zu, der dafür zuständig ist: CellValueFactory (CVF), CellFactory (CF), Prädikat der
FilteredList(FL) oder Bindung des Comparators derSortedList(SL). (6 Punkte)
| Nr. | Wunsch oder Codezeile | Baustein |
|---|---|---|
| 1 | spalteDatum.setCellValueFactory(z -> new ReadOnlyObjectWrapper<>(z.getValue().datum())) |
|
| 2 | Eine leere Notiz soll als grauer Strich „–“ erscheinen. | |
| 3 | Ein Klick auf den Kopf der Spalte „frühestens“ soll die Gesuche nach dieser Uhrzeit ordnen. | |
| 4 | Es sollen nur Gesuche mit der Richtung „zur Schule“ zu sehen sein. | |
| 5 | setText(leer \|\| wert == null ? null : wert.format(DATUM)); |
|
| 6 | Die Spalte „Haltepunkt“ soll den Namen des Haltepunkts aus dem Record Gesuch zeigen. |
- Im Controller stehen nur diese beiden Zeilen, eine Bindung des Comparators fehlt. Beschreibe, was Ben sieht, wenn er auf den Kopf der Spalte „frühestens“ klickt, und gib die fehlende Zeile an. (3 Punkte)
gesucheTabelle.setItems(viewModel.getPassendeGesuche()); // eine SortedList ohne Comparator
gesucheTabelle.getSortOrder().add(spalteFruehestens);Ein Teammitglied lässt die CellValueFactory der Spalte „Datum“ gleich den fertigen Text liefern:
z -> new ReadOnlyStringWrapper(z.getValue().datum().format(DATUM)). Die Anzeige sieht danach genauso aus. Beschreibe, was beim Sortieren nach der Spalte mit den Gesuchen vom 09.11.2026 und vom 15.10.2026 passiert, und erläutere, wie Wert und Darstellung besser getrennt werden. (3 Punkte)Nenne zwei Gründe, warum das Team die CellValueFactory als Lambda schreibt und nicht als
new PropertyValueFactory<>("datum"). (2 Punkte)
- Je Zeile 1 Punkt:
| Nr. | Wunsch oder Codezeile | Baustein |
|---|---|---|
| 1 | setCellValueFactory(…datum()) |
CVF: liefert den Wert der Zeile für die Spalte |
| 2 | leere Notiz als grauer Strich | CF: Darstellung in updateItem, Text „–“ und CSS-Klasse |
| 3 | Klick auf „frühestens“ ordnet | SL: Comparator der SortedList an tabelle.comparatorProperty() gebunden |
| 4 | nur „zur Schule“ | FL: Prädikat der FilteredList |
| 5 | setText(… wert.format(DATUM)) |
CF: Zeile aus updateItem einer eigenen Zelle |
| 6 | Name des Haltepunkts | CVF: z -> new ReadOnlyStringWrapper(z.getValue().haltepunkt()) |
- Ben sieht keine Änderung der Reihenfolge (1 Punkt): Die Tabelle versucht zu sortieren, stellt fest, dass sie eine
SortedListmit ungebundenem Comparator nicht selbst umsortieren darf, setzt die Sortierung zurück (der Pfeil im Spaltenkopf verschwindet wieder) und schreibt nur einen Hinweis ins Log (1 Punkt). Die fehlende Zeile (1 Punkt):
viewModel.getPassendeGesuche().comparatorProperty().bind(gesucheTabelle.comparatorProperty());Die Spalte sortiert dann Texte zeichenweise: „09.11.2026“ steht vor „15.10.2026“, obwohl der 15. Oktober früher ist (1 Punkt). Besser liefert die CellValueFactory den Wert
LocalDate(danach sortiert die Tabelle) (1 Punkt), und eine CellFactory, etwanew FormatierteZelle<>(d -> d.format(DATUM)), formatiert nur die Anzeige (1 Punkt).Je Grund 1 Punkt, z. B.:
- Der Compiler prüft Methode und Typ; ein Tippfehler im Namen fiele bei
PropertyValueFactoryerst zur Laufzeit als leere Spalte auf. PropertyValueFactoryarbeitet per Reflexion; im Modulsystem müsste das Paket des Models fürjavafx.basegeöffnet werden.- Ein Lambda kann auch abgeleitete Werte liefern, etwa nur die Uhrzeit oder einen zusammengesetzten Text.
Aufgabe 2.1: Die Mitfahrgesuche als Tabelle anzeigen (Kernkompetenz)
(ca. 15 min Bearbeitungszeit)
Aylin Kaya testet das Gesuche-Fenster und wünscht sich im Replenishment: „Ich will die Gesuche nach Uhrzeit sortieren und sehen, ob jemand eine Notiz geschrieben hat, ohne jede Zeile zu lesen.“ Das Team ersetzt die ListView durch eine Tabelle.
Ersetze in
gesuche.fxmldieListView#gesucheListedurch eineTableView#gesucheTabellemit den Spalten „Datum“, „frühestens“, „spätestens“, „Haltepunkt“ und „Notiz“ (fx:idz. B.spalteDatum). Die Spalten sollen die ganze Breite ausfüllen; ohne Treffer zeigt die Tabelle „Keine offenen Gesuche für diese Auswahl.“ (4 Punkte)Ergänze im
GesucheControllerdie Spaltenfelder mit passenden Typparametern und setze für alle fünf Spalten eine CellValueFactory. Das Datum erscheint im Format16.11.2026, eine leere Notiz als grauer Strich „–“ (CSS-Klasseohne-notiz). Achte darauf, dass Zellen wiederverwendet werden. (6 Punkte)Sorge dafür, dass ein Klick auf einen Spaltenkopf sortiert und die Tabelle anfangs nach „frühestens“ aufsteigend sortiert ist. Der feste Comparator im
GesucheViewModelentfällt dafür. (3 Punkte)Passe den Controller-Test an: Er prüft, dass die Tabelle fünf Spalten hat und die Gesuche nach dem Laden in der Reihenfolge ihrer frühesten Uhrzeit zeigt. (3 Punkte)
- Tabelle in
gesuche.fxml(Importe fürTableViewundTableColumnergänzen; Spalten 2 Punkte, Breite und Platzhalter je 1 Punkt):
<TableView fx:id="gesucheTabelle" VBox.vgrow="ALWAYS">
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY_FLEX_LAST_COLUMN"/>
</columnResizePolicy>
<placeholder>
<Label text="Keine offenen Gesuche für diese Auswahl."/>
</placeholder>
<columns>
<TableColumn fx:id="spalteDatum" text="Datum" prefWidth="95"/>
<TableColumn fx:id="spalteFruehestens" text="frühestens" prefWidth="85"/>
<TableColumn fx:id="spalteSpaetestens" text="spätestens" prefWidth="85"/>
<TableColumn fx:id="spalteHaltepunkt" text="Haltepunkt" prefWidth="200"/>
<TableColumn fx:id="spalteNotiz" text="Notiz" prefWidth="160"/>
</columns>
</TableView>- Felder und Zellen im Controller (Typparameter 1 Punkt, CellValueFactorys 2 Punkte, Datum 1 Punkt, Notiz-Zelle mit leerem Fall und Entfernen der Klasse 2 Punkte):
private static final DateTimeFormatter DATUM = DateTimeFormatter.ofPattern("dd.MM.yyyy");
@FXML private TableView<Gesuch> gesucheTabelle;
@FXML private TableColumn<Gesuch, LocalDate> spalteDatum;
@FXML private TableColumn<Gesuch, LocalTime> spalteFruehestens;
@FXML private TableColumn<Gesuch, LocalTime> spalteSpaetestens;
@FXML private TableColumn<Gesuch, String> spalteHaltepunkt;
@FXML private TableColumn<Gesuch, String> spalteNotiz;
private void tabelleEinrichten() {
// CellValueFactory: welcher Wert aus dem Gesuch in die Spalte kommt
spalteDatum.setCellValueFactory(z -> new ReadOnlyObjectWrapper<>(z.getValue().datum()));
spalteFruehestens.setCellValueFactory(z -> new ReadOnlyObjectWrapper<>(z.getValue().fruehestens()));
spalteSpaetestens.setCellValueFactory(z -> new ReadOnlyObjectWrapper<>(z.getValue().spaetestens()));
spalteHaltepunkt.setCellValueFactory(z -> new ReadOnlyStringWrapper(z.getValue().haltepunkt()));
spalteNotiz.setCellValueFactory(z -> new ReadOnlyStringWrapper(z.getValue().notiz()));
// CellFactory: Darstellung; sortiert wird weiter nach LocalDate
spalteDatum.setCellFactory(spalte -> new FormatierteZelle<>(datum -> datum.format(DATUM)));
spalteNotiz.setCellFactory(spalte -> new TableCell<>() {
@Override
protected void updateItem(String notiz, boolean leer) {
super.updateItem(notiz, leer);
getStyleClass().remove("ohne-notiz"); // Zelle kann vorher eine leere Notiz gezeigt haben
if (leer || notiz == null) {
setText(null);
} else if (notiz.isBlank()) {
setText("–");
getStyleClass().add("ohne-notiz");
} else {
setText(notiz);
}
}
});
// … Sortierung siehe c)
}In gesuche.css:
.table-cell.ohne-notiz {
-fx-text-fill: #8a8a8a;
}- Im ViewModel entsteht die
SortedListohne Comparator, und der Getter liefert den TypSortedList, damit der Controller den Comparator binden kann (1 Punkt). Im Controller (Bindung 1 Punkt, Startsortierung 1 Punkt):
// GesucheViewModel
private final SortedList<Gesuch> passend = new SortedList<>(gefiltert);
public SortedList<Gesuch> getPassendeGesuche() {
return passend;
}
// GesucheController.tabelleEinrichten()
gesucheTabelle.setItems(viewModel.getPassendeGesuche());
viewModel.getPassendeGesuche().comparatorProperty().bind(gesucheTabelle.comparatorProperty());
gesucheTabelle.getSortOrder().add(spalteFruehestens);- Test (Laden abwarten 1 Punkt, Prüfungen je 1 Punkt). Mit dem Filter aus Aufgabe 2.2 zeigt die Tabelle nach dem Laden die drei Gesuche „zur Schule“; ohne Aufgabe 2.2 wählst Du im Test vorher Haltepunkt und Datum:
@Test
@SuppressWarnings("unchecked")
void tabelleZeigtGesucheZurSchuleNachFruehestensSortiert() throws Exception {
Parent wurzel = ladeGesuche(); // lädt gesuche.fxml mit Beispielgesuchen und wartet auf den Task
TableView<Gesuch> tabelle = (TableView<Gesuch>) wurzel.lookup("#gesucheTabelle");
assertEquals(5, tabelle.getColumns().size());
assertEquals(List.of(3L, 1L, 5L), tabelle.getItems().stream().map(Gesuch::id).toList());
}Gesuch 3 beginnt um 06:45 Uhr, Gesuch 1 und 5 um 07:00 Uhr; Gesuch 1 steht vor 5, weil SortedList bei gleichen Werten die Reihenfolge der Quelle behält.
Aufgabe 2.2: Die Mitfahrgesuche filtern (Kernkompetenz)
(ca. 13 min Bearbeitungszeit)
Mit der Tabelle wird der Filter zum Hindernis: Solange Haltepunkt und Datum nicht gewählt sind, bleibt sie leer, und die Spalten „Datum“ und „Haltepunkt“ zeigen in jeder Zeile dasselbe. Aylin möchte zuerst alle Gesuche ihrer Richtung sehen und dann eingrenzen, etwa nach Notizen wie „Rad“.
Ändere das
GesucheViewModel: Haltepunkt und Datum sind optionale Filter (nullheißt „alle“), die Richtung bleibt Pflicht. Eine neueStringProperty notizSuchefiltert nach einem Teil der Notiz, ohne Rücksicht auf Groß- und Kleinschreibung und Leerzeichen am Rand. Der Hinweis lautet bei Treffern „n von m offenen Gesuchen“,zuruecksetzen()setzt auch die Suche zurück. Das BindingfilterVollstaendigentfällt. (6 Punkte)Ergänze in der Maske ein Textfeld
#notizSuchemit der Beschriftung „Notiz enthält“ (Tastenkürzel Alt+N) und binde es an das ViewModel. (2 Punkte)Schreibe drei Komponententests für das ViewModel: ohne Haltepunkt und Datum alle Gesuche „zur Schule“ mit passendem Hinweis; die Suche „ RAD “ findet genau Gesuch 1; ein Comparator, der von außen gesetzt wird, sortiert die gefilterte Liste. (5 Punkte)
Erläutere, warum sich die Sortierung in c) ohne Tabelle testen lässt, obwohl in der App die Tabelle den Comparator setzt. (1 Punkt)
- ViewModel (Property und Abhängigkeiten 1 Punkt, Prädikat mit optionalen Filtern 3 Punkte, Hinweis 1 Punkt, Zurücksetzen 1 Punkt):
private final StringProperty notizSuche = new SimpleStringProperty(this, "notizSuche", "");
public GesucheViewModel(GesuchQuelle quelle, Executor hintergrund) {
this.quelle = quelle;
this.hintergrund = hintergrund;
gefiltert.predicateProperty().bind(Bindings.createObjectBinding(
this::filter, haltepunkt, zurSchule, datum, notizSuche));
hinweis = Bindings.createStringBinding(() -> {
if (laedt.get()) {
return "Gesuche werden geladen …";
}
if (fehler.get() != null) {
return "Gesuche nicht geladen: " + fehler.get();
}
return passend.isEmpty() ? "Keine offenen Gesuche für diese Auswahl."
: passend.size() + " von " + gesuche.size() + " offenen Gesuchen";
}, laedt, fehler, passend, gesuche);
}
/** Ein Filter, der nicht gesetzt ist (null bzw. leer), lässt alle Gesuche durch. */
private Predicate<Gesuch> filter() {
String richtung = zurSchule.get() ? "zur-schule" : "nach-hause";
String halt = haltepunkt.get();
LocalDate tag = datum.get();
String suche = notizSuche.get() == null ? "" : notizSuche.get().strip().toLowerCase(Locale.GERMANY);
return g -> g.richtung().equals(richtung)
&& (halt == null || g.haltepunkt().equals(halt))
&& (tag == null || g.datum().equals(tag))
&& g.notiz().toLowerCase(Locale.GERMANY).contains(suche);
}
public void zuruecksetzen() {
haltepunkt.set(null);
datum.set(null);
zurSchule.set(true);
notizSuche.set("");
}
public StringProperty notizSucheProperty() {
return notizSuche;
}Die Werte der Properties werden einmal gelesen, bevor das Prädikat entsteht; so prüft das Lambda bei jedem Gesuch dieselben Werte. Der Controller markiert den Hinweis nur noch bei einem Ladefehler mit der Klasse fehler, weil es keinen „unvollständigen“ Filter mehr gibt.
- Maske und Bindung (je 1 Punkt):
<TextField fx:id="notizSuche" promptText="z. B. Rad" prefWidth="220"
GridPane.columnIndex="1" GridPane.rowIndex="3"/>
<Label text="_Notiz enthält" mnemonicParsing="true" labelFor="$notizSuche"
GridPane.columnIndex="0" GridPane.rowIndex="3"/>@FXML private TextField notizSuche;
// in initialize():
notizSuche.textProperty().bindBidirectional(viewModel.notizSucheProperty());Der Button „Zurücksetzen“ rutscht in die Zeile 4. Bidirektional ist nötig, damit zuruecksetzen() auch das Textfeld leert.
- Tests (erster und zweiter je 2 Punkte, dritter 1 Punkt); das ViewModel lädt in
@BeforeEachdie fünfBeispielgesuchemitRunnable::runals Executor:
private List<Long> ids() {
return viewModel.getPassendeGesuche().stream().map(Gesuch::id).toList();
}
@Test
void ohneHaltepunktUndDatumZeigtAlleGesucheDerRichtung() {
assertEquals(List.of(1L, 3L, 5L), ids());
assertEquals("3 von 5 offenen Gesuchen", viewModel.hinweisProperty().get());
}
@Test
void notizSucheIgnoriertGrossKleinUndLeerzeichen() {
viewModel.notizSucheProperty().set(" RAD ");
assertEquals(List.of(1L), ids());
}
@Test
void comparatorDerTabelleSortiertDieGefilterteListe() {
// so, wie es die Bindung an tabelle.comparatorProperty() nach Klick auf „frühestens“ tut
viewModel.getPassendeGesuche().setComparator(Comparator.comparing(Gesuch::fruehestens));
assertEquals(List.of(3L, 1L, 5L), ids());
}- Die Tabelle setzt über die Bindung nur einen
Comparatorin dieSortedList; im Test setztsetComparator(…)denselben Wert direkt. Das ViewModel kennt die Tabelle nicht, deshalb reicht ein normaler JUnit-Test ohne Fenster (1 Punkt).
Aufgabe 3.1: Durchlaufzeiten eines Boards auswerten (Profilkompetenz)
(ca. 16 min Bearbeitungszeit)
Das Team, das die Gesuche-Ansicht baut, führt ein eigenes Board mit den Tickets GES-1 bis GES-8 und zählt die Durchlaufzeit wie im Artikel: Projekttag „Fertig am“ minus Projekttag „Gezogen am“ plus 1. Thomas Becker möchte im nächsten Replenishment wissen, womit er bei neuen Tickets rechnen kann.
| Ticket | Gezogen am | Fertig am | Notiz auf dem Board | Durchlaufzeit |
|---|---|---|---|---|
| GES-1 | 2 | 4 | – | |
| GES-2 | 3 | 7 | 2 Projekttage fertig in „Test“, niemand frei zum Prüfen | |
| GES-3 | 5 | 7 | – | |
| GES-4 | 6 | 13 | 4 Projekttage blockiert: Endpunkt GET /gesuche fehlte im Backend |
|
| GES-5 | 8 | 10 | – | |
| GES-6 | 9 | 12 | 1 Projekttag fertig in „Test“ | |
| GES-7 | 11 | 13 | – | |
| GES-8 | 12 | 15 | – |
Berechne die Durchlaufzeit jedes Tickets sowie Mittelwert, Median und Spannweite der acht Werte. (6 Punkte)
Beurteile, ob Mittelwert oder Median das typische Ticket dieses Boards besser beschreibt, und begründe mit GES-4. (3 Punkte)
Thomas Becker fragt: „Wenn ihr heute ein neues Standard-Ticket zieht, wann ist es fertig?“ Formuliere eine Antwort auf Grundlage der acht Durchlaufzeiten und erläutere, warum „im Schnitt nach 4,1 Tagen“ keine gute Antwort ist. (3 Punkte)
Bestimme, wie viele der Projekttage in den Durchlaufzeiten auf Warten oder Blockaden entfallen, und leite zwei Regeln oder Maßnahmen für das Board ab, die die Durchlaufzeit verkürzen, ohne dass jemand schneller programmieren muss. (5 Punkte)
- Durchlaufzeiten (je zwei richtige Werte 1 Punkt, zusammen 4 Punkte; Mittelwert und Median 1 Punkt, Spannweite 1 Punkt):
| Ticket | Gezogen am | Fertig am | Rechnung | Durchlaufzeit |
|---|---|---|---|---|
| GES-1 | 2 | 4 | 4 − 2 + 1 | 3 |
| GES-2 | 3 | 7 | 7 − 3 + 1 | 5 |
| GES-3 | 5 | 7 | 7 − 5 + 1 | 3 |
| GES-4 | 6 | 13 | 13 − 6 + 1 | 8 |
| GES-5 | 8 | 10 | 10 − 8 + 1 | 3 |
| GES-6 | 9 | 12 | 12 − 9 + 1 | 4 |
| GES-7 | 11 | 13 | 13 − 11 + 1 | 3 |
| GES-8 | 12 | 15 | 15 − 12 + 1 | 4 |
- Mittelwert: (3 + 5 + 3 + 8 + 3 + 4 + 3 + 4) / 8 = 33 / 8 ≈ 4,1 Projekttage
- Median: sortiert 3, 3, 3, 3, 4, 4, 5, 8; bei acht Werten liegt er zwischen dem 4. und 5. Wert: (3 + 4) / 2 = 3,5 Projekttage
- Spannweite: 3 bis 8 Projekttage (Differenz 5)
Der Median beschreibt das typische Ticket besser (1 Punkt). GES-4 ist ein Ausreißer: Es war vier Tage blockiert und hebt den Mittelwert allein um etwa einen halben Tag (ohne GES-4: 25 / 7 ≈ 3,6) (1 Punkt). Der Median bleibt mit und ohne GES-4 bei 3 bis 3,5 Tagen, weil er nur von der Lage der mittleren Werte abhängt; sechs von acht Tickets brauchten 3 oder 4 Tage (1 Punkt).
Eine mögliche Antwort (2 Punkte): „Von acht Tickets waren sechs nach höchstens 4 und sieben nach höchstens 5 Projekttagen fertig. Wir rechnen also mit 5 Projekttagen; nur wenn das Ticket an einer Blockade hängt wie GES-4, kann es länger dauern.“ Ein Mittelwert von 4,1 Tagen klingt genau, sagt aber nichts über die Streuung (1 Punkt): Zwei der acht Tickets dauerten länger, eines doppelt so lange, und den Mittelwert selbst hat kein einziges Ticket erreicht. Thomas Becker würde sich auf einen Termin verlassen, der für einen Teil der Tickets nicht hält. Eine Aussage über einen Anteil der Tickets (etwa 7 von 8, knapp 90 %) zeigt, wie sicher die Zusage ist. Mit acht Tickets ist sie noch unsicher und wird mit jedem Ticket besser.
Wartezeit (1 Punkt): 2 + 4 + 1 = 7 der 33 Projekttage, also gut ein Fünftel, entfallen auf Warten und Blockaden; in dieser Zeit arbeitete niemand an den Tickets.
Je Regel oder Maßnahme mit Bezug zu den Daten 2 Punkte, z. B.:
- Gegen das Warten in „Test“ (GES-2, GES-6): Von rechts nach links schauen und zuerst prüfen, bevor jemand ein neues Ticket zieht; wer frei wird, prüft. Alternativ ein kleineres WIP-Limit für „In Arbeit“, damit Kapazität für „Test“ frei bleibt.
- Gegen Blockaden durch fehlende Endpunkte (GES-4): Abhängigkeiten zum Backend schon im Replenishment prüfen und einen fehlenden Endpunkt früh als Ticket an das Backend-Team geben (im Projekt als Expedite-Ticket); ein Ticket mit offener Abhängigkeit nicht nach „Bereit“ holen. Bis der Endpunkt da ist, kann gegen WireMock entwickelt werden.
- Blockierte Tickets im täglichen Kanban-Meeting zuerst besprechen und eine Person benennen, die die Blockade löst.
Links und weitere Informationen
zum Artikel: TableView für Buchungen: anzeigen, filtern, sortieren und bestätigen
zum Board: Kanban-Projektstart für die Desktop-App
als Nachschlagewerk: JavaFX-API: Klasse TableColumn (CellValueFactory, CellFactory)
als Nachschlagewerk: JavaFX-API: Klasse SortedList
für den Einstieg: Szenario Mitfahrlabor mit Personas und Regeln
Hinweis zur Nachnutzung als Open Educational Resource (OER)
Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: “Übungsaufgaben zur TableView: Mitfahrgesuche als Tabelle und Durchlaufzeiten” von oer-informatik.de (H. Stein), Lizenz: CC BY 4.0. Der Artikel wurde unter https://oer-informatik.de/javafx-tableview-buchungen-uebungsaufgaben veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/javafx/javafx-einstieg. Erstellt mit KI-Assistenz; von mir umfassend redaktionell überarbeitet, geprüft und freigegeben. Stand: 11.11.2026.
[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]
