quarto datei mit refactoring und programm principles

This commit is contained in:
2025-05-28 14:49:06 +02:00
parent 3ae042e0bb
commit d56c7a1439

516
luca.qmd Normal file
View File

@ -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 Dont 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
}