diff --git a/luca.qmd b/luca.qmd new file mode 100644 index 0000000..ad6a066 --- /dev/null +++ b/luca.qmd @@ -0,0 +1,516 @@ +--- +title: "Programmentwurf AdvancedSoftwareEngineering" +author: + - Luca Müller + - Paul Martin +date: 05/31/2025 +date-format: "DD.MM.YYYY" +lang: de +format: + pdf: + toc: true + number-sections: true + colorlinks: true +--- + +# Code Smell 1: Long Method + +## Beschreibung des Problems + +Das **Long Method** Anti-Pattern tritt auf, wenn eine einzelne Methode zu viele Zeilen Code enthält und multiple Verantwortlichkeiten übernimmt. Eine solche Methode verstößt gegen das **Single Responsibility Principle (SRP)** und führt zu verschiedenen Problemen: + +- **Schwere Verständlichkeit**: Entwickler müssen viel Zeit aufwenden, um die gesamte Methode zu durchdringen +- **Reduzierte Testbarkeit**: Verschiedene Logikbereiche können nicht isoliert getestet werden +- **Mangelnde Wiederverwendbarkeit**: Teilfunktionalitäten sind nicht separat nutzbar +- **Erhöhte Fehleranfälligkeit**: Bugs sind schwerer zu lokalisieren und zu beheben + +## Praktisches Beispiel + +Die folgende Methode `stopRunFromBackend()` zeigt ein typisches Long Method Problem: + +### Vorher (Problematischer Code) + +```java +public static void stopRunFromBackend(String message){ + // Zustandsänderungen + isAutoRunActive = false; + if (isSleeping) + wakeUpFromSleep(); + else + DataRegister.resetPC(); + + // UI-Erstellung und -Konfiguration + Stage stoppedStage = new Stage(); + stoppedStage.setTitle("Programm unterbrochen!"); + VBox vbox = new VBox(); + vbox.setAlignment(javafx.geometry.Pos.CENTER); + Label grundlabel = new Label("Grund: " + message); + grundlabel.setStyle("-fx-font-size: 16px; -fx-font-weight: bold;"); + Label ueberlabel = new Label("Programm unterbrochen!"); + vbox.getChildren().add(ueberlabel); + vbox.getChildren().add(grundlabel); + VBox.setMargin(grundlabel, new javafx.geometry.Insets(10, 10, 10, 10)); + Scene scene = new Scene(vbox, 300, 90); + stoppedStage.setAlwaysOnTop(true); + stoppedStage.setScene(scene); + stoppedStage.show(); +} +``` + +### Identifizierte Probleme + +- **Vermischte Verantwortlichkeiten**: Die Methode kombiniert Geschäftslogik (Zustandsänderungen) mit UI-Code +- **Schwere Testbarkeit**: UI-Code kann nicht isoliert von der Geschäftslogik getestet werden +- **Code-Duplikation**: Wird die Dialog-Funktionalität anderswo benötigt, muss der gesamte UI-Code kopiert werden + +## Lösung: Extract Method Refactoring + +Die **Extract Method** Technik löst das Problem durch Aufspaltung der langen Methode in kleinere, fokussierte Methoden: + +### Nachher (Refactorierter Code) + +```java +public static void stopRunFromBackend(String message){ + handleExecutionStop(); + showInterruptionDialog(message); +} + +private static void handleExecutionStop(){ + isAutoRunActive = false; + if (isSleeping) + wakeUpFromSleep(); + else + DataRegister.resetPC(); +} + +private static void showInterruptionDialog(String message){ + Stage stoppedStage = new Stage(); + stoppedStage.setTitle("Programm unterbrochen!"); + + VBox vbox = new VBox(); + vbox.setAlignment(javafx.geometry.Pos.CENTER); + + Label ueberlabel = new Label("Programm unterbrochen!"); + Label grundlabel = new Label("Grund: " + message); + grundlabel.setStyle("-fx-font-size: 16px; -fx-font-weight: bold;"); + + vbox.getChildren().addAll(ueberlabel, grundlabel); + VBox.setMargin(grundlabel, new javafx.geometry.Insets(10, 10, 10, 10)); + + Scene scene = new Scene(vbox, 300, 90); + stoppedStage.setAlwaysOnTop(true); + stoppedStage.setScene(scene); + stoppedStage.show(); +} +``` + +## Vorteile der Lösung + +- **Klare Trennung der Verantwortlichkeiten**: Geschäftslogik und UI-Code sind getrennt +- **Verbesserte Lesbarkeit**: Jede Methode hat einen klaren, fokussierten Zweck +- **Erhöhte Wiederverwendbarkeit**: `showInterruptionDialog()` kann in anderen Kontexten genutzt werden +- **Bessere Testbarkeit**: Geschäftslogik und UI können separat getestet werden + +--- + +# Code Smell 2: Large Class + +## Beschreibung des Problems + +Eine **Large Class** entsteht, wenn eine Klasse zu viele Verantwortlichkeiten übernimmt und dadurch überladen wird. Typische Kennzeichen sind: + +- **Hohe Anzahl an Instanzvariablen**: Die Klasse verwaltet zu viele verschiedene Datentypen +- **Viele Methoden**: Unterschiedliche Funktionsbereiche werden in einer Klasse gemischt +- **Multiple Domänenaspekte**: Logik, Darstellung, Benutzerinteraktion und Statusverwaltung in einer Klasse + +## Praktisches Beispiel + +Die Klasse `Controller_Frontend` zeigt typische Large Class Probleme: + +### Identifizierte Probleme + +```java +public class Controller_Frontend { + // GUI-Elemente + @FXML private Button startButton; + @FXML private Button pauseButton; + @FXML private Label statusLabel; + + // Zustandsverwaltung (gehört nicht hierher!) + private static boolean isAutoRunActive = false; + private static boolean isSleeping = false; + private static double executionTimeMultiplier = 1.0; + + // GUI-Steuerung + public void handleStart() { /* ... */ } + public void handlePause() { /* ... */ } + + // Zustandslogik (gehört nicht hierher!) + public void sleep() { isSleeping = true; } + public void wakeUpFromSleep() { isSleeping = false; } + + // ... viele weitere Methoden +} +``` + +### Auswirkungen + +- **Schwere Wartbarkeit**: Änderungen in einem Bereich können unbeabsichtigte Nebeneffekte verursachen +- **Reduzierte Testbarkeit**: Verschiedene Aspekte können nicht isoliert getestet werden +- **Unübersichtlichkeit**: Die Klasse wird schnell unhandlich und schwer verständlich +- **Violierung des Single Responsibility Principle**: Eine Klasse sollte nur einen Grund zur Änderung haben + +## Lösung: Extract Class Refactoring + +Die Lösung besteht in der Auslagerung der Zustandsverwaltung in eine separate, spezialisierte Klasse: + +### Neue ExecutionState Klasse + +```java +/** + * Zentrale Verwaltung aller Ausführungszustände. + * Diese Klasse kapselt alle zustandsbezogenen Operationen + * und bietet eine saubere API für den Zugriff darauf. + */ +public class ExecutionState { + private static boolean isAutoRunActive = false; + private static boolean isSleeping = false; + private static double executionTimeMultiplier = 1.0; + + // AutoRun-Zustand + public static boolean isAutoRunActive() { + return isAutoRunActive; + } + + public static void setAutoRunActive(boolean active) { + isAutoRunActive = active; + } + + // Sleep-Zustand + public static boolean isSleeping() { + return isSleeping; + } + + public static void sleep() { + isSleeping = true; + } + + public static void wakeUp() { + isSleeping = false; + } + + // Ausführungsgeschwindigkeit + public static double getExecutionTimeMultiplier() { + return executionTimeMultiplier; + } + + public static void setExecutionTimeMultiplier(double multiplier) { + if (multiplier > 0) { + executionTimeMultiplier = multiplier; + } + } + + // Hilfsmethoden + public static void reset() { + isAutoRunActive = false; + isSleeping = false; + executionTimeMultiplier = 1.0; + } +} +``` + +### Refactorierte Controller Klasse + +```java +public class Controller_Frontend { + // Nur noch GUI-Elemente + @FXML private Button startButton; + @FXML private Button pauseButton; + @FXML private Label statusLabel; + + // Verwendung der ExecutionState Klasse + public void stopRunFromBackend(String message) { + ExecutionState.setAutoRunActive(false); + + if (ExecutionState.isSleeping()) { + ExecutionState.wakeUp(); + } else { + DataRegister.resetPC(); + } + + showInterruptionDialog(message); + } + + // Weitere GUI-Methoden... +} +``` + +## Vorteile der Lösung + +- **Klare Verantwortlichkeiten**: Controller fokussiert sich auf GUI, ExecutionState auf Zustandsverwaltung +- **Verbesserte Testbarkeit**: Zustandslogik kann isoliert getestet werden +- **Erhöhte Wiederverwendbarkeit**: ExecutionState kann in anderen Klassen genutzt werden +- **Bessere Wartbarkeit**: Änderungen an der Zustandslogik betreffen nur eine Klasse + +--- + +# Code Smell 3: Shotgun Surgery + +## Beschreibung des Problems + +**Shotgun Surgery** tritt auf, wenn eine kleine fachliche Änderung Modifikationen an vielen verschiedenen Stellen im Code erfordert. Dieses Anti-Pattern entsteht durch: + +- **Zu starke Verteilung**: Zusammengehörige Funktionalität ist über viele Klassen und Methoden verstreut +- **Mangelnde Kapselung**: Ähnliche Operationen sind nicht zentral gebündelt +- **Duplizierte Logik**: Gleiche oder ähnliche Code-Fragmente existieren an mehreren Stellen + +## Praktisches Beispiel + +Die Schlafmodus-Funktionalität war ursprünglich über mehrere Bereiche verteilt: + +### Vor dem Refactoring (Problematische Verteilung) + +```java +// In Controller_Frontend +private boolean isSleeping = false; + +public void enterSleepMode() { + isSleeping = true; + // Logging hier + System.out.println("Entering sleep mode"); +} + +// In ExecutionEngine +private boolean sleepState = false; + +public void pauseExecution() { + sleepState = true; + // Ähnliches Logging dort + System.out.println("Execution paused - sleep mode"); +} + +// In StatusManager +public void checkSleepStatus() { + if (someCondition) { + // Wieder ähnlicher Code + setSleeping(true); + System.out.println("Sleep mode activated"); + } +} +``` + +### Identifizierte Probleme + +- **Mehrfache Implementierung**: Sleep-Logik existiert in verschiedenen Varianten +- **Inkonsistente Zustände**: Verschiedene Klassen können unterschiedliche Sleep-Zustände haben +- **Hoher Änderungsaufwand**: Neue Sleep-Features müssen an mehreren Stellen implementiert werden +- **Fehleranfälligkeit**: Beim Hinzufügen neuer Funktionen können leicht Stellen vergessen werden + +## Lösung: Zentralisierung durch Extract Class + +Die Lösung besteht in der Konsolidierung aller sleep-bezogenen Operationen in der `ExecutionState` Klasse: + +### Zentralisierte Lösung + +```java +public class ExecutionState { + private static boolean isSleeping = false; + private static final Logger logger = LoggerFactory.getLogger(ExecutionState.class); + + /** + * Aktiviert den Schlafmodus mit einheitlichem Logging und Zustandsmanagement + */ + public static void sleep() { + if (!isSleeping) { + isSleeping = true; + logger.info("Sleep mode activated"); + notifyStateChange("SLEEP_ACTIVATED"); + } + } + + /** + * Deaktiviert den Schlafmodus + */ + public static void wakeUp() { + if (isSleeping) { + isSleeping = false; + logger.info("Waking up from sleep mode"); + notifyStateChange("SLEEP_DEACTIVATED"); + } + } + + /** + * Überprüft den aktuellen Schlafzustand + */ + public static boolean isSleeping() { + return isSleeping; + } + + /** + * Erweiterte Sleep-Funktionalität mit Timeout + */ + public static void sleepWithTimeout(long milliseconds) { + sleep(); + Timer timer = new Timer(); + timer.schedule(new TimerTask() { + @Override + public void run() { + wakeUp(); + } + }, milliseconds); + } + + private static void notifyStateChange(String event) { + // Zentrale Benachrichtigung für alle interessierten Komponenten + EventBus.getInstance().publish(new StateChangeEvent(event)); + } +} +``` + +### Vereinfachte Nutzung in anderen Klassen + +```java +// In Controller_Frontend +public void handleSleepButton() { + ExecutionState.sleep(); + updateUI(); +} + +// In ExecutionEngine +public void pauseIfNeeded() { + if (shouldPause()) { + ExecutionState.sleep(); + } +} + +// In StatusManager +public void checkAndActivateSleep() { + if (criticalCondition()) { + ExecutionState.sleepWithTimeout(5000); // 5 Sekunden Sleep + } +} +``` + +## Vorteile der Lösung + +- **Einheitliche Implementierung**: Alle sleep-bezogenen Operationen verwenden dieselbe Logik +- **Zentrale Wartung**: Änderungen müssen nur an einer Stelle vorgenommen werden +- **Konsistente Zustände**: Nur eine Quelle der Wahrheit für den Sleep-Zustand +- **Erweiterte Funktionalität**: Neue Features (wie Timeout) können zentral hinzugefügt werden +- **Bessere Nachverfolgbarkeit**: Einheitliches Logging und Event-System + +--- + +--- +title: "Anwendung von Programmierprinzipien im Projekt" +format: html +--- + +## Einleitung +Im Rahmen der Refaktorisierung und Weiterentwicklung des Projekts wurde besonderer Fokus auf die Einhaltung zentraler Programmierprinzipien gelegt. Die folgenden Prinzipien wurden gezielt analysiert und angewendet: + +## 1. SOLID-Prinzipien + +### Single Responsibility Principle (SRP) +Die Methode `stopRunFromBackend()` wurde in zwei unabhängige Methoden aufgeteilt: + +```java +public void stopRunFromBackend(String message) { + ExecutionState.setAutoRunActive(false); + handleSleepOrReset(); + showStopDialog(message); +} + +private void handleSleepOrReset() { + if (ExecutionState.isSleeping()) { + ExecutionState.wakeUp(); + } else { + dataRegister.resetPC(); + } +} + +private static void showStopDialog(String message) { + // GUI-Erzeugung... +} +``` + +### Open/Closed Principle (OCP) +Die Klasse `ExecutionState` kapselt erweiterbare Zustandslogik, ohne dass bestehende Methoden geändert werden müssen: + +```java +public class ExecutionState { + private static boolean isAutoRunActive; + private static boolean isSleeping; + + public static boolean isSleeping() { return isSleeping; } + public static void wakeUp() { isSleeping = false; } + public static void setAutoRunActive(boolean value) { + isAutoRunActive = value; + } +} +``` + +### Liskov Substitution Principle (LSP) +`Controller_Frontend` implementiert ein Interface, wodurch die Substituierbarkeit gemäß LSP gewährleistet ist: + +```java +public class Controller_Frontend extends PICComponent implements FrontendControllerInterface { } + +public void resetPC() { + programCounter = 0; +} +``` + +### Interface Segregation Principle (ISP) +Spezialisierte Interfaces sorgen dafür, dass Klassen nur relevante Funktionen implementieren: + +```java +public interface TimerInterface { + void start(); + void stop(); + int getCurrentTime(); +} +``` + +### Dependency Inversion Principle (DIP) +Zentrale Komponenten wie `PICComponentLocator` und Interfaces lösen die Abhängigkeit von konkreten Klassen: + +```java +private PICComponentLocator picComponents; +``` + +## 2. GRASP-Prinzipien + +### Low Coupling +Die Kopplung im Projekt ist durch Verwendung zentraler Zugriffsklassen wie `PICComponentLocator` gering gehalten. + +```java +private PICComponentLocator picComponents; +``` + +### High Cohesion +Funktionen übernehmen jeweils thematisch zusammenhängende Aufgaben: + +```java +public static void showStopDialog(String message) { + Stage stoppedStage = new Stage(); + VBox vbox = new VBox(); + Label grundlabel = new Label("Grund: " + message); + // GUI-Details ausgelassen + stoppedStage.show(); +} +``` + +## 3. DRY – Don’t Repeat Yourself +Wiederverwendbare Logik zur Statuskontrolle wurde in `ExecutionState` gekapselt. GUI-Erzeugung findet zentral in `showStopDialog()` statt: + +```java +public class ExecutionState { + private static boolean isSleeping; + public static boolean isSleeping() { return isSleeping; } + public static void wakeUp() { isSleeping = false; } +} + +private static void showStopDialog(String message) { + // einmal zentral definierte GUI-Logik +}