539 lines
21 KiB
C#
539 lines
21 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Project_Periodensystem.Model;
|
|
using Project_Periodensystem.Persistence;
|
|
|
|
namespace Project_Periodensystem.Controller
|
|
{
|
|
/// <summary>
|
|
/// Haupt-Controller für das Periodensystem - implementiert MVC-Pattern.
|
|
/// Koordiniert Geschäftslogik zwischen View und Model/Persistence.
|
|
/// Delegiert komplexe Aufgaben an spezialisierte Helper-Klassen.
|
|
/// </summary>
|
|
public class PeriodensystemController
|
|
{
|
|
// ===== PRIVATE FELDER =====
|
|
|
|
/// <summary>
|
|
/// Liste aller chemischen Elemente
|
|
/// </summary>
|
|
private List<Element> _elements = new List<Element>();
|
|
|
|
/// <summary>
|
|
/// Navigation Service für Seitenwechsel (optional, da Interface-basiert)
|
|
/// </summary>
|
|
private readonly INavigationService? _navigationService;
|
|
|
|
// ===== HELPER-KLASSEN FÜR CODE-ORGANISATION =====
|
|
|
|
/// <summary>
|
|
/// Spezialisierte Klasse für Validierungslogik
|
|
/// Nullable: Wird erst nach dem Laden der Elemente initialisiert
|
|
/// Separation of Concerns: Validierung ist getrennt von anderen Aufgaben
|
|
/// </summary>
|
|
private ElementValidator? _validator;
|
|
|
|
/// <summary>
|
|
/// Spezialisierte Klasse für statistische Berechnungen
|
|
/// Nullable: Wird erst nach dem Laden der Elemente initialisiert
|
|
/// Single Responsibility: Nur für Statistiken zuständig
|
|
/// </summary>
|
|
private ElementStatistics? _statistics;
|
|
|
|
// ===== KONSTRUKTOR =====
|
|
/// <summary>
|
|
/// Konstruktor mit optionaler Navigation
|
|
/// </summary>
|
|
public PeriodensystemController(INavigationService? navigationService = null)
|
|
{
|
|
_navigationService = navigationService;
|
|
LoadElements(); // Daten sofort beim Erstellen laden
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lädt alle chemischen Elemente und initialisiert Helper-Klassen
|
|
/// </summary>
|
|
private void LoadElements()
|
|
{
|
|
try
|
|
{
|
|
// Elemente aus DataManager laden
|
|
_elements = DataManager.LoadElements();
|
|
Logger.Log($"Controller: {_elements.Count} Elemente erfolgreich geladen");
|
|
|
|
// Helper-Klassen initialisieren
|
|
_validator = new ElementValidator(_elements);
|
|
_statistics = new ElementStatistics(_elements);
|
|
|
|
// Demonstrative Logging der geladenen Daten
|
|
var symbols = GetAllSymbols();
|
|
Logger.LogArray("Element-Symbole (erste 10)", symbols.Take(10).ToArray());
|
|
|
|
var (totalElements, uniqueSeries, avgWeight) = GetStatistics();
|
|
Logger.LogTuple("Statistiken", $"Elemente: {totalElements}, Serien: {uniqueSeries}, Ø-Gewicht: {avgWeight:F2}");
|
|
|
|
var (lightest, heaviest) = GetWeightExtremes();
|
|
if (lightest != null && heaviest != null)
|
|
{
|
|
Logger.LogTuple("Gewichts-Extreme", $"Leichtestes: {lightest.Symbol} ({lightest.AtomicWeight}), Schwerstes: {heaviest.Symbol} ({heaviest.AtomicWeight})");
|
|
}
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Graceful Degradation: Bei Fehlern eine leere Liste verwenden
|
|
Logger.Log($"EXCEPTION in LoadElements: {ex.Message}");
|
|
_elements = new List<Element>();
|
|
|
|
// Helper-Klassen können nicht initialisiert werden (bleiben null)
|
|
_validator = null;
|
|
_statistics = null;
|
|
}
|
|
}
|
|
|
|
// ===== PUBLIC API METHODEN - ELEMENTZUGRIFF =====
|
|
|
|
/// <summary>
|
|
/// Gibt alle Elemente als List<Element> zurück
|
|
///
|
|
/// RÜCKGABETYP: List<Element>
|
|
/// - Ermöglicht Add/Remove-Operationen
|
|
/// - LINQ-Operationen verfügbar
|
|
/// - Dynamische Größe
|
|
///
|
|
/// VERWENDUNG:
|
|
/// - Hauptsächlich für UI-Bindungen (WPF/Avalonia DataBinding)
|
|
/// - Für LINQ-Abfragen in der View-Schicht
|
|
/// </summary>
|
|
public List<Element> GetAllElements()
|
|
{
|
|
return _elements;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt alle Elemente als Array zurück
|
|
///
|
|
/// RÜCKGABETYP: Element[]
|
|
/// - Feste Größe (Read-Only nach Erstellung)
|
|
/// - Bessere Performance für Iterationen
|
|
/// - Weniger Speicher-Overhead als List
|
|
///
|
|
/// C# KONZEPT: LINQ ToArray()
|
|
/// - Konvertiert IEnumerable zu Array
|
|
/// - Erstellt eine Kopie der Daten
|
|
///
|
|
/// VERWENDUNG:
|
|
/// - Für Algorithmen, die feste Arrays benötigen
|
|
/// - Wenn Unveränderlichkeit wichtig ist
|
|
/// </summary>
|
|
public Element[] GetAllElementsAsArray()
|
|
{
|
|
return _elements.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extrahiert alle Element-Symbole als String-Array
|
|
///
|
|
/// ZWECK:
|
|
/// - Demonstriert klassische Array-Initialisierung und -Population
|
|
/// - Zeigt manuelle Iteration vs. LINQ-Alternativen
|
|
///
|
|
/// C# KONZEPTE:
|
|
/// - Array-Initialisierung: new string[size]
|
|
/// - For-Schleifen: Klassische imperative Programmierung
|
|
/// - Array-Indexing: array[i] = value
|
|
///
|
|
/// ALTERNATIVE (LINQ):
|
|
/// return _elements.Select(e => e.Symbol).ToArray();
|
|
///
|
|
/// LERNWERT:
|
|
/// - Zeigt Unterschied zwischen imperativer und funktionaler Programmierung
|
|
/// - Arrays vs. Collections
|
|
/// </summary>
|
|
public string[] GetAllSymbols()
|
|
{
|
|
// Klassische Array-Erstellung mit fester Größe
|
|
string[] symbols = new string[_elements.Count];
|
|
|
|
// Manuelle Population mit for-Schleife (imperativer Stil)
|
|
for (int i = 0; i < _elements.Count; i++)
|
|
{
|
|
symbols[i] = _elements[i].Symbol;
|
|
}
|
|
|
|
return symbols;
|
|
}
|
|
|
|
// ===== DELEGATION AN HELPER-KLASSEN =====
|
|
|
|
/// <summary>
|
|
/// Diese Methoden implementieren das Delegation Pattern
|
|
///
|
|
/// VORTEILE DER DELEGATION:
|
|
/// - Single Responsibility Principle: Jede Klasse hat einen klaren Zweck
|
|
/// - Code-Reduktion: Controller wird schlanker und übersichtlicher
|
|
/// - Testbarkeit: Helper-Klassen können isoliert getestet werden
|
|
/// - Wartbarkeit: Änderungen an Validierung/Statistik beeinflussen Controller nicht
|
|
///
|
|
/// C# KONZEPTE:
|
|
/// - Null-Conditional Operator (?.): Verhindert NullReferenceException
|
|
/// - Null-Coalescing Operator (??): Liefert Fallback-Werte wenn null
|
|
/// - Lambda Expressions: Kurze Syntax für einfache Delegationen
|
|
/// - Expression-bodied Members: => Syntax für einzeilige Methoden
|
|
///
|
|
/// FEHLERBEHANDLUNG:
|
|
/// - Alle Methoden handhaben null-Helper graceful mit Fallback-Werten
|
|
/// - Keine Exceptions, sondern sichere Default-Werte
|
|
/// </summary>
|
|
|
|
// ===== VALIDIERUNGS-METHODEN (delegiert an ElementValidator) =====
|
|
|
|
/// <summary>
|
|
/// Validiert die gesamte Element-Datenbasis
|
|
/// Delegation an ElementValidator.ValidateData()
|
|
/// Fallback: false bei null-Validator
|
|
/// </summary>
|
|
public bool ValidateData() => _validator?.ValidateData() ?? false;
|
|
|
|
/// <summary>
|
|
/// Validiert die Position eines spezifischen Elements im Periodensystem
|
|
/// Delegation an ElementValidator.ValidateElementPosition()
|
|
/// Fallback: false bei null-Validator
|
|
/// </summary>
|
|
public bool ValidateElementPosition(Element? element) => _validator?.ValidateElementPosition(element) ?? false;
|
|
|
|
// ===== STATISTIK-METHODEN (delegiert an ElementStatistics) =====
|
|
|
|
/// <summary>
|
|
/// Extrahiert alle Atomgewichte als Array
|
|
/// Delegation an ElementStatistics.GetAtomicWeights()
|
|
/// Fallback: leeres Array bei null-Statistics
|
|
/// </summary>
|
|
public double[] GetAtomicWeights() => _statistics?.GetAtomicWeights() ?? new double[0];
|
|
|
|
/// <summary>
|
|
/// Berechnet das durchschnittliche Atomgewicht
|
|
/// Delegation an ElementStatistics.GetAverageAtomicWeight()
|
|
/// Fallback: 0.0 bei null-Statistics
|
|
/// </summary>
|
|
public double GetAverageAtomicWeight() => _statistics?.GetAverageAtomicWeight() ?? 0.0;
|
|
|
|
/// <summary>
|
|
/// Sammelt alle einzigartigen Element-Serien
|
|
/// Delegation an ElementStatistics.GetAllSeries()
|
|
/// Fallback: leere Liste bei null-Statistics
|
|
/// </summary>
|
|
public List<string> GetAllSeries() => _statistics?.GetAllSeries() ?? new List<string>();
|
|
|
|
/// <summary>
|
|
/// Sammelt wichtige Statistiken in einem Tuple
|
|
/// Tuple Deconstruction: (int, int, double) für strukturierte Daten
|
|
/// Delegation an ElementStatistics.GetStatistics()
|
|
/// Fallback: (0, 0, 0.0) bei null-Statistics
|
|
/// </summary>
|
|
public (int totalElements, int uniqueSeries, double avgWeight) GetStatistics() => _statistics?.GetStatistics() ?? (0, 0, 0.0);
|
|
|
|
/// <summary>
|
|
/// Findet das leichteste und schwerste Element
|
|
/// Tuple mit nullable Elementen: (Element?, Element?)
|
|
/// Delegation an ElementStatistics.GetWeightExtremes()
|
|
/// Fallback: (null, null) bei null-Statistics
|
|
/// </summary>
|
|
public (Element? lightest, Element? heaviest) GetWeightExtremes() => _statistics?.GetWeightExtremes() ?? (null, null);
|
|
|
|
/// <summary>
|
|
/// Extrahiert grundlegende Informationen eines Elements
|
|
/// Tuple mit gemischten Typen: (string, string, double)
|
|
/// Delegation an ElementStatistics.GetElementInfo()
|
|
/// Fallback: ("?", "Unknown", 0.0) bei null-Statistics
|
|
/// </summary>
|
|
public (string symbol, string name, double weight) GetElementInfo(int atomicNumber) => _statistics?.GetElementInfo(atomicNumber) ?? ("?", "Unknown", 0.0);
|
|
|
|
/// <summary>
|
|
/// Ermittelt die Position eines Elements im Periodensystem
|
|
/// Tuple mit Koordinaten: (int row, int column)
|
|
/// Delegation an ElementStatistics.GetElementPosition()
|
|
/// Fallback: (-1, -1) für "nicht gefunden" bei null-Statistics
|
|
/// </summary>
|
|
public (int row, int column) GetElementPosition(int atomicNumber) => _statistics?.GetElementPosition(atomicNumber) ?? (-1, -1);
|
|
|
|
// ===== ELEMENT-SUCHFUNKTIONEN =====
|
|
|
|
/// <summary>
|
|
/// Sucht ein Element anhand seiner Ordnungszahl (Atomzahl)
|
|
///
|
|
/// ZWECK:
|
|
/// - Eindeutige Identifizierung von Elementen
|
|
/// - Ordnungszahl ist der Primärschlüssel im Periodensystem
|
|
///
|
|
/// C# KONZEPTE:
|
|
/// - LINQ FirstOrDefault(): Gibt erstes Element oder null zurück
|
|
/// - Lambda Expression: e => e.AtomicNumber == atomicNumber
|
|
/// - Nullable Return Type: Element? kann null sein
|
|
///
|
|
/// PARAMETER:
|
|
/// - atomicNumber: Ordnungszahl des gesuchten Elements (1-118)
|
|
///
|
|
/// RÜCKGABE:
|
|
/// - Element-Objekt wenn gefunden, null wenn nicht vorhanden
|
|
///
|
|
/// VERWENDUNG:
|
|
/// var hydrogen = controller.GetElementByAtomicNumber(1);
|
|
/// if (hydrogen != null) { /* Element verwenden */ }
|
|
/// </summary>
|
|
public Element? GetElementByAtomicNumber(int atomicNumber)
|
|
{
|
|
return _elements.FirstOrDefault(e => e.AtomicNumber == atomicNumber);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sucht ein Element anhand seines chemischen Symbols
|
|
///
|
|
/// ZWECK:
|
|
/// - Benutzerfreundliche Suche mit bekannten Symbolen (H, He, Li, etc.)
|
|
/// - Case-insensitive Suche für bessere Usability
|
|
///
|
|
/// C# KONZEPTE:
|
|
/// - String.IsNullOrWhiteSpace(): Robuste Null/Empty-Prüfung
|
|
/// - StringComparison.OrdinalIgnoreCase: Ignoriert Groß-/Kleinschreibung
|
|
/// - Early Return Pattern: Frühzeitiger Ausstieg bei ungültigen Eingaben
|
|
/// - Guard Clauses: Validierung am Methodenstart
|
|
///
|
|
/// PARAMETER:
|
|
/// - symbol: Chemisches Symbol (z.B. "H", "he", "LI")
|
|
///
|
|
/// RÜCKGABE:
|
|
/// - Element-Objekt wenn gefunden, null bei ungültigem Symbol oder nicht gefunden
|
|
///
|
|
/// FEHLERBEHANDLUNG:
|
|
/// - Null/Empty/Whitespace-Eingaben werden sicher behandelt
|
|
/// - Keine Exception bei ungültigen Eingaben
|
|
/// </summary>
|
|
public Element? GetElementBySymbol(string symbol)
|
|
{
|
|
// Guard Clause: Ungültige Eingaben frühzeitig abfangen
|
|
if (string.IsNullOrWhiteSpace(symbol)) return null;
|
|
|
|
// Case-insensitive Suche für bessere Benutzerfreundlichkeit
|
|
return _elements.FirstOrDefault(e => string.Equals(e.Symbol, symbol, StringComparison.OrdinalIgnoreCase));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Filtert alle Elemente einer bestimmten chemischen Serie
|
|
///
|
|
/// ZWECK:
|
|
/// - Gruppierung von Elementen nach chemischen Eigenschaften
|
|
/// - Ermöglicht Analyse von Element-Familien (Metalle, Nichtmetalle, etc.)
|
|
///
|
|
/// C# KONZEPTE:
|
|
/// - LINQ Where(): Filtert Elemente basierend auf Bedingung
|
|
/// - ToList(): Konvertiert IEnumerable zu List für weitere Operationen
|
|
/// - Guard Clause: Schutz vor ungültigen Eingaben
|
|
///
|
|
/// PARAMETER:
|
|
/// - series: Name der chemischen Serie (z.B. "Alkali metal", "Noble gas")
|
|
///
|
|
/// RÜCKGABE:
|
|
/// - Liste aller Elemente der angegebenen Serie
|
|
/// - Leere Liste bei ungültiger Serie oder wenn keine Elemente gefunden
|
|
///
|
|
/// BEISPIEL:
|
|
/// var alkaliMetals = controller.GetElementsBySeries("Alkali metal");
|
|
/// // Enthält: Li, Na, K, Rb, Cs, Fr
|
|
/// </summary>
|
|
public List<Element> GetElementsBySeries(string series)
|
|
{
|
|
// Guard Clause: Ungültige Eingaben behandeln
|
|
if (string.IsNullOrWhiteSpace(series)) return new List<Element>();
|
|
|
|
// LINQ-Filterung mit case-insensitive Vergleich
|
|
return _elements.Where(e => string.Equals(e.Series, series, StringComparison.OrdinalIgnoreCase)).ToList();
|
|
}
|
|
|
|
// ===== DATEN-MANAGEMENT =====
|
|
|
|
/// <summary>
|
|
/// Lädt alle Elementdaten neu aus dem Persistence-Layer
|
|
///
|
|
/// ZWECK:
|
|
/// - Aktualisierung bei geänderten Datenquellen
|
|
/// - Recovery nach Datenfehlern
|
|
/// - Synchronisation mit externen Datenänderungen
|
|
///
|
|
/// ABLAUF:
|
|
/// 1. Benutzer über Neuladen informieren
|
|
/// 2. LoadElements() ruft gesamte Initialisierungslogik auf
|
|
/// 3. Helper-Klassen werden mit neuen Daten reinitialisiert
|
|
///
|
|
/// VERWENDUNG:
|
|
/// - Nach Datenimport
|
|
/// - Bei Corruption Recovery
|
|
/// - Für Entwicklungs-/Debug-Zwecke
|
|
/// </summary>
|
|
public void RefreshData()
|
|
{
|
|
Logger.Log("Daten werden neu geladen...");
|
|
LoadElements(); // Vollständige Reinitialisierung
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt die aktuelle Anzahl geladener Elemente zurück
|
|
///
|
|
/// ZWECK:
|
|
/// - Schnelle Verfügbarkeitsprüfung
|
|
/// - UI-Statusanzeigen
|
|
/// - Validierung nach Datenoperationen
|
|
///
|
|
/// C# KONZEPT:
|
|
/// - Expression-bodied Member: => Syntax für einzeilige Properties
|
|
/// - Count Property: Effiziente Größenabfrage für List<T>
|
|
///
|
|
/// RÜCKGABE:
|
|
/// - Anzahl der verfügbaren Elemente (0 wenn keine geladen)
|
|
/// </summary>
|
|
public int GetElementCount() => _elements.Count;
|
|
|
|
// ===== NAVIGATION CONTROLLER METHODEN =====
|
|
|
|
/// <summary>
|
|
/// Diese Methoden implementieren den Command Pattern für UI-Aktionen
|
|
///
|
|
/// ARCHITEKTUR-PRINZIPIEN:
|
|
/// - Separation of Concerns: UI-Logik getrennt von Datenlogik
|
|
/// - Command Pattern: Jede Benutzeraktion wird als Method-Call gekapselt
|
|
/// - Exception Safety: Alle Navigation-Aktionen sind exception-safe
|
|
/// - Dependency Injection: NavigationService wird injiziert, nicht direkt instanziiert
|
|
///
|
|
/// C# KONZEPTE:
|
|
/// - Try-Catch Blocks: Robuste Fehlerbehandlung
|
|
/// - Null-Conditional Operator (?.): Sichere Methodenaufrufe
|
|
/// - Method Chaining: navigationService?.Method()
|
|
/// - Consistent Error Handling: Alle Methoden folgen demselben Pattern
|
|
///
|
|
/// LOGGING:
|
|
/// - Alle Aktionen werden geloggt für Debugging
|
|
/// - Exceptions werden mit Kontext geloggt
|
|
/// - Einheitliche Log-Messages für bessere Nachverfolgung
|
|
/// </summary>
|
|
|
|
/// <summary>
|
|
/// Navigiert zur Hauptansicht des Periodensystems
|
|
///
|
|
/// ZWECK:
|
|
/// - Wechsel zur interaktiven Periodensystem-Darstellung
|
|
/// - Hauptfunktion der Anwendung
|
|
///
|
|
/// FEHLERBEHANDLUNG:
|
|
/// - Exception wird geloggt, aber nicht weitergegeben
|
|
/// - Navigation Service kann null sein (optional dependency)
|
|
/// - Graceful Degradation bei Navigationsfehlern
|
|
/// </summary>
|
|
public void HandleNavigateToPeriodicTable()
|
|
{
|
|
try
|
|
{
|
|
Logger.Log("Controller: Navigation zum Periodensystem angefordert");
|
|
_navigationService?.NavigateToPeriodicTable();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogException(ex, "HandleNavigateToPeriodicTable");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Navigiert zur Informationsseite über die Anwendung
|
|
///
|
|
/// ZWECK:
|
|
/// - Anzeige von Anwendungsinformationen
|
|
/// - Credits und Versionsinformationen
|
|
/// - Hilfe und Dokumentation
|
|
/// </summary>
|
|
public void HandleNavigateToAbout()
|
|
{
|
|
try
|
|
{
|
|
Logger.Log("Controller: Navigation zu About angefordert");
|
|
_navigationService?.NavigateToAbout();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogException(ex, "HandleNavigateToAbout");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Navigiert zurück zur Startseite/Landing Page
|
|
///
|
|
/// ZWECK:
|
|
/// - Rückkehr zum Hauptmenü
|
|
/// - Reset der Anwendung zum Ausgangszustand
|
|
/// - Home-Button Funktionalität
|
|
/// </summary>
|
|
public void HandleNavigateToLanding()
|
|
{
|
|
try
|
|
{
|
|
Logger.Log("Controller: Navigation zur Landing Page angefordert");
|
|
_navigationService?.NavigateToLanding();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogException(ex, "HandleNavigateToLanding");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wechselt zwischen hellen und dunklen UI-Themes
|
|
///
|
|
/// ZWECK:
|
|
/// - Benutzerfreundlichkeit durch Theme-Auswahl
|
|
/// - Accessibility (Dunkel-Modus für Augen-schonend)
|
|
/// - Moderne UI-Standards erfüllen
|
|
///
|
|
/// UI-PATTERN:
|
|
/// - Toggle-Funktionalität (Hell ↔ Dunkel)
|
|
/// - Persistierung des gewählten Themes
|
|
/// </summary>
|
|
public void HandleToggleTheme()
|
|
{
|
|
try
|
|
{
|
|
Logger.Log("Controller: Theme-Wechsel angefordert");
|
|
_navigationService?.ToggleTheme();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogException(ex, "HandleToggleTheme");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Startet den Datenexport-Prozess
|
|
///
|
|
/// ZWECK:
|
|
/// - Export der Elementdaten in verschiedene Formate
|
|
/// - Datensicherung und -portabilität
|
|
/// - Integration mit anderen Anwendungen
|
|
///
|
|
/// UI-WORKFLOW:
|
|
/// - Zeigt Bestätigungsdialog an
|
|
/// - Benutzer wählt Export-Optionen
|
|
/// - Export wird durchgeführt und bestätigt
|
|
/// </summary>
|
|
public void HandleExportData()
|
|
{
|
|
try
|
|
{
|
|
Logger.Log("Controller: Daten-Export angefordert");
|
|
_navigationService?.ShowExportConfirmation();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogException(ex, "HandleExportData");
|
|
}
|
|
}
|
|
}
|
|
}
|