Refactor components for improved instantiation and error handling

This commit is contained in:
tiko
2026-02-27 12:42:45 +01:00
parent 5cbdca7f58
commit 770fa86998
12 changed files with 44 additions and 28 deletions

View File

@@ -1,5 +1,5 @@
#IHK Calc Auto-Save #IHK Calc Auto-Save
#Thu Feb 26 09:21:47 CET 2026 #Fri Feb 27 12:33:57 CET 2026
AP1=3 AP1=3
AP2_DOCUMENTATION=1 AP2_DOCUMENTATION=1
AP2_PART_1=4 AP2_PART_1=4

View File

@@ -26,7 +26,7 @@ public final class ExamCalcApp {
* @param args Die beim Programmstart übergebenen Argumente (werden nicht verwendet). * @param args Die beim Programmstart übergebenen Argumente (werden nicht verwendet).
*/ */
public static void main(String[] args) { public static void main(String[] args) {
// Erstellt eine neue Instant des MainFrames mit dem Titel "IHK-Notenberechner" und öffnet diesen Frame von alleine. // Erstellt eine neue Instanz der Klasse "MainFrame" mit dem Titel "IHK-Notenberechner" und öffnet diesen Frame von alleine.
new MainFrame("IHK-Notenberechner", true); new MainFrame("IHK-Notenberechner", true);
} }
} }

View File

@@ -23,6 +23,11 @@ public record Grading(ExamRecord examRecord) {
*/ */
public static final int NEEDED_EXAM_RESULTS = 7; public static final int NEEDED_EXAM_RESULTS = 7;
/**
* Die Note, die zur automatischen "Nicht-Bestehung" der Prüfung führt.
*/
public static final double TERMINATION_GRADE = 6.0;
/** /**
* Der Schwellenwert für die Note, bis zu dem eine Prüfung als bestanden gilt * Der Schwellenwert für die Note, bis zu dem eine Prüfung als bestanden gilt
*/ */

View File

@@ -17,32 +17,26 @@ public enum Exam {
* Die erste Abschlussprüfung (Teil 1), gewichtet mit 20 %. * Die erste Abschlussprüfung (Teil 1), gewichtet mit 20 %.
*/ */
AP1("Abschlussprüfung 1", 0.2), AP1("Abschlussprüfung 1", 0.2),
/** /**
* Der erste schriftliche Teil der Abschlussprüfung 2, gewichtet mit 10 %. * Der erste schriftliche Teil der Abschlussprüfung 2, gewichtet mit 10 %.
*/ */
AP2_PART_1("AP2 Teil 1 (Schriftlich)", 0.1), AP2_PART_1("AP2 Teil 1 (Schriftlich)", 0.1),
/** /**
* Der zweite schriftliche Teil der Abschlussprüfung 2, gewichtet mit 10 %. * Der zweite schriftliche Teil der Abschlussprüfung 2, gewichtet mit 10 %.
*/ */
AP2_PART_2("AP2 Teil 2 (Schriftlich)", 0.1), AP2_PART_2("AP2 Teil 2 (Schriftlich)", 0.1),
/** /**
* Der dritte schriftliche Teil der Abschlussprüfung 2 (WiSo), gewichtet mit 10 %. * Der dritte schriftliche Teil der Abschlussprüfung 2 (WiSo), gewichtet mit 10 %.
*/ */
AP2_PART_3("AP2 Teil 3 (Wirtschafts- und Sozialkunde)", 0.1), AP2_PART_3("AP2 Teil 3 (Wirtschafts- und Sozialkunde)", 0.1),
/** /**
* Die Dokumentation der praktischen Arbeit in AP2, gewichtet mit 25 %. * Die Dokumentation der praktischen Arbeit in AP2, gewichtet mit 25 %.
*/ */
AP2_DOCUMENTATION("AP2 Dokumentation (Praktisch)", 0.25), AP2_DOCUMENTATION("AP2 Dokumentation (Praktisch)", 0.25),
/** /**
* Die Präsentation der praktischen Arbeit in AP2, gewichtet mit 12,5 %. * Die Präsentation der praktischen Arbeit in AP2, gewichtet mit 12,5 %.
*/ */
AP2_PRESENTATION("AP2 Präsentation (Praktisch)", 0.125), AP2_PRESENTATION("AP2 Präsentation (Praktisch)", 0.125),
/** /**
* Das fachliche Gespräch zur praktischen Arbeit in AP2, gewichtet mit 12,5 %. * Das fachliche Gespräch zur praktischen Arbeit in AP2, gewichtet mit 12,5 %.
*/ */
@@ -50,7 +44,6 @@ public enum Exam {
private final String localizedName; private final String localizedName;
private final double gradeTotal; private final double gradeTotal;
/** /**
* Erstellt einen neuen Prüfungsbestandteil. * Erstellt einen neuen Prüfungsbestandteil.
* *

View File

@@ -79,7 +79,7 @@ public final class ExamRecord {
* @return {@code true}, wenn mindestens eine Note 6.0 enthalten ist. * @return {@code true}, wenn mindestens eine Note 6.0 enthalten ist.
*/ */
public boolean terminated() { public boolean terminated() {
return results.stream().anyMatch(entry -> entry.grade() == 6.0); return results.stream().anyMatch(entry -> entry.grade() == Grading.TERMINATION_GRADE);
} }
/** /**

View File

@@ -34,7 +34,7 @@ public record ExamResult(
*/ */
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat( private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat(
"0", "0",
DecimalFormatSymbols.getInstance(Locale.GERMAN) DecimalFormatSymbols.getInstance(Locale.ROOT)
); );
/* /*

View File

@@ -45,7 +45,7 @@ public final class LocalStorage {
try (BufferedWriter writer = Files.newBufferedWriter(STORAGE_PATH)) { try (BufferedWriter writer = Files.newBufferedWriter(STORAGE_PATH)) {
data.store(writer, "IHK Calc Auto-Save"); data.store(writer, "IHK Calc Auto-Save");
} catch (IOException exception) { } catch (IOException exception) {
System.err.println("Exception occured while saving data to storage: " + exception.getMessage()); System.err.println("Exception occurred while saving data to storage: " + exception.getMessage());
} }
} }

View File

@@ -88,7 +88,7 @@ public final class MainFrame extends JFrame {
setAppIcon(); setAppIcon();
initFrame(); initFrame();
addWindowListener(new WindowClosingComponent(examInputFields)); addWindowListener(WindowClosingComponent.hook(examInputFields));
} }
/** /**
@@ -145,7 +145,7 @@ public final class MainFrame extends JFrame {
Properties savedData = LocalStorage.load(); Properties savedData = LocalStorage.load();
for (Exam exam : Exam.values()) { for (Exam exam : Exam.values()) {
InputComponent inputComponent = new InputComponent(); InputComponent inputComponent = InputComponent.create();
String savedValue = savedData.getProperty(exam.name()); String savedValue = savedData.getProperty(exam.name());
if (savedValue != null) { if (savedValue != null) {
@@ -166,11 +166,7 @@ public final class MainFrame extends JFrame {
resultTextArea.setBackground(Theme.RESULT_BG); resultTextArea.setBackground(Theme.RESULT_BG);
resultTextArea.setBorder(new EmptyBorder(10, 12, 10, 12)); resultTextArea.setBorder(new EmptyBorder(10, 12, 10, 12));
new ButtonComponent( ButtonComponent.place(calcButton, resultTextArea, examInputFields);
calcButton,
resultTextArea,
examInputFields
);
} }
/** /**

View File

@@ -58,7 +58,7 @@ public final class ButtonComponent implements ActionListener {
* @param resultTextArea Der Textbereich zur Anzeige der Ergebnisse oder Fehlermeldungen. * @param resultTextArea Der Textbereich zur Anzeige der Ergebnisse oder Fehlermeldungen.
* @param examInputFields Eine Map, die jedem Prüfungsteil sein entsprechendes Eingabefeld zuordnet. * @param examInputFields Eine Map, die jedem Prüfungsteil sein entsprechendes Eingabefeld zuordnet.
*/ */
public ButtonComponent( private ButtonComponent(
JButton calcButton, JButton calcButton,
JTextArea resultTextArea, JTextArea resultTextArea,
Map<Exam, JTextField> examInputFields Map<Exam, JTextField> examInputFields
@@ -70,6 +70,21 @@ public final class ButtonComponent implements ActionListener {
styleCalcButton(); styleCalcButton();
} }
/**
* Erstellt eine neue {@code CalcButtonComponent} und verknüpft sie mit der UI.
*
* @param calcButton Der Button, der die Berechnung auslösen soll.
* @param resultTextArea Der Textbereich zur Anzeige der Ergebnisse oder Fehlermeldungen.
* @param examInputFields Eine Map, die jedem Prüfungsteil sein entsprechendes Eingabefeld zuordnet.
*/
public static void place(
JButton calcButton,
JTextArea resultTextArea,
Map<Exam, JTextField> examInputFields
) {
new ButtonComponent(calcButton, resultTextArea, examInputFields);
}
/** /**
* Konfiguriert das visuelle Erscheinungsbild des Buttons basierend auf dem globalen Theme. * Konfiguriert das visuelle Erscheinungsbild des Buttons basierend auf dem globalen Theme.
* *

View File

@@ -49,11 +49,7 @@ public final class CardComponent {
Theme.TEXT_MUTED Theme.TEXT_MUTED
); );
card.setBorder(new CompoundBorder( card.setBorder(new CompoundBorder(titled, new EmptyBorder(8, 10, 10, 10)));
titled,
new EmptyBorder(8, 10, 10, 10)
));
content.setBackground(Theme.PANEL_BG); content.setBackground(Theme.PANEL_BG);
card.add(content, BorderLayout.CENTER); card.add(content, BorderLayout.CENTER);

View File

@@ -31,7 +31,6 @@ public final class InputComponent extends JTextField {
new LineBorder(Theme.BORDER_COL, 2, true), new LineBorder(Theme.BORDER_COL, 2, true),
new EmptyBorder(6, 10, 6, 10) new EmptyBorder(6, 10, 6, 10)
); );
/** /**
* Der Rahmen der Komponente, wenn sie den Tastaturfokus erhält. * Der Rahmen der Komponente, wenn sie den Tastaturfokus erhält.
*/ */
@@ -40,6 +39,14 @@ public final class InputComponent extends JTextField {
new EmptyBorder(6, 10, 6, 10) new EmptyBorder(6, 10, 6, 10)
); );
/**
* Erstellt einen InputComponent mit einem statischen Constructor.
* @return eine Instanz eines InputComponents.
*/
public static InputComponent create() {
return new InputComponent();
}
/** /**
* Erstellt eine neue {@code InputComponent} und initialisiert das visuelle Erscheinungsbild. * Erstellt eine neue {@code InputComponent} und initialisiert das visuelle Erscheinungsbild.
* *
@@ -50,7 +57,7 @@ public final class InputComponent extends JTextField {
* </p> * </p>
* *
*/ */
public InputComponent() { private InputComponent() {
super(); super();
setFont(Theme.FONT_FIELD); setFont(Theme.FONT_FIELD);

View File

@@ -31,10 +31,14 @@ public class WindowClosingComponent extends WindowAdapter {
* *
* @param examInputFields Die Map, die den Zugriff auf die aktuellen Noteneingaben ermöglicht. * @param examInputFields Die Map, die den Zugriff auf die aktuellen Noteneingaben ermöglicht.
*/ */
public WindowClosingComponent(Map<Exam, JTextField> examInputFields) { private WindowClosingComponent(Map<Exam, JTextField> examInputFields) {
this.examInputFields = examInputFields; this.examInputFields = examInputFields;
} }
public static WindowClosingComponent hook(Map<Exam, JTextField> examInputFields) {
return new WindowClosingComponent(examInputFields);
}
/** /**
* Wird aufgerufen, wenn das Fenster geschlossen wird. * Wird aufgerufen, wenn das Fenster geschlossen wird.
* *