CS_LEVEL_19_SOLUTION - OnlyCook/abitur-elite-code GitHub Wiki

Level 19 – Musterlösung: Missions-Zentrale

Lösung

public class LogEintrag
{
    private string typ;
    private string inhalt;
 
    public LogEintrag(string typ, string inhalt)
    {
        this.typ = typ;
        this.inhalt = inhalt;
    }
}
 
public class WartungsTicket
{
    private string roverId;
    private string grund;
 
    public WartungsTicket(string id, string grund)
    {
        this.roverId = id;
        this.grund = grund;
    }
}
 
public class WartungsDienst
{
    private List<WartungsTicket> tickets;
 
    public WartungsDienst()
    {
        this.tickets = new List<WartungsTicket>();
    }
 
    public void ErstelleTicket(string roverId, string grund)
    {
        tickets.Add(new WartungsTicket(roverId, grund));
    }
}
 
public class Rover
{
    private string id;
    private int batterie = 100;
    private List<LogEintrag> logs;
 
    public Rover(string id)
    {
        this.id = id;
        this.logs = new List<LogEintrag>();
    }
 
    public bool VerarbeiteStatus(string typ, string val)
    {
        switch (typ)
        {
            case "ERR":
                batterie -= 5;
                logs.Add(new LogEintrag(typ, val));
                break;
            case "BAT":
                batterie = int.Parse(val);
                break;
        }
        return batterie < 20;
    }
 
    public string GetId()
    {
        return id;
    }
}
 
public class MissionsZentrale
{
    private List<Rover> roverListe;
    private WartungsDienst wartung;
 
    public MissionsZentrale()
    {
        this.roverListe = new List<Rover>();
        this.wartung = new WartungsDienst();
    }
 
    public void AddRover(Rover r)
    {
        roverListe.Add(r);
    }
 
    public void VerarbeiteDatenstrom(string stream)
    {
        string[] eintraege = stream.Split('|');
        int i = 0;
        while (i < eintraege.Length)
        {
            string[] teile = eintraege[i].Split('#');
            int k = 0;
            while (k < roverListe.Count)
            {
                Rover r = roverListe[k];
                if (r.GetId() == teile[0])
                {
                    bool istKritisch = r.VerarbeiteStatus(teile[1], teile[2]);
                    if (istKritisch)
                    {
                        wartung.ErstelleTicket(r.GetId(), "Kritischer Batteriestatus");
                    }
                }
                k++;
            }
            i++;
        }
    }
}

Erklärung

Das Abschlusslevel von Sektion 4

Dieses Level kombiniert alles aus der Sektion: String-Zerlegung, Switch-Logik, Objekt-Kollaboration und die Übertragung eines Struktogramms in Code – diesmal mit fünf Klassen auf einmal. LogEintrag, WartungsTicket und WartungsDienst sind reine Datenklassen ohne neue Konzepte. Die interessante Logik steckt in Rover und MissionsZentrale.


Rover – Zustand verwalten und zurückmelden

public bool VerarbeiteStatus(string typ, string val)
{
    switch (typ)
    {
        case "ERR":
            batterie -= 5;
            logs.Add(new LogEintrag(typ, val));
            break;
        case "BAT":
            batterie = int.Parse(val);
            break;
    }
    return batterie < 20;
}

Die Methode verändert den internen Zustand des Rovers und gibt danach direkt batterie < 20 zurück – ohne extra if. Das ist eine kompakte Schreibweise: Der Vergleich ergibt selbst einen bool, der direkt zurückgegeben werden kann.

int.Parse(val) wandelt den String-Wert (z.B. "85") in eine Ganzzahl um – nötig, weil alle Teile des Datenstroms als Strings ankommen.

Das batterie-Attribut hat einen Initialwert direkt bei der Deklaration:

private int batterie = 100;

Das Klassendiagramm schreibt batterie : int = 100 vor – dieser Startwert muss also nicht im Konstruktor gesetzt werden, sondern steht direkt an der Deklaration. Beides wäre korrekt, aber die Notation im Diagramm deutet auf diese kompaktere Variante hin.


VerarbeiteDatenstrom() – das Struktogramm umsetzen

Das Struktogramm gibt die Struktur exakt vor: zwei verschachtelte while-Schleifen mit manuellem Index-Zähler.

Erster Split: den Strom aufteilen

string[] eintraege = stream.Split('|');

Der Datenstrom "CUR#ERR#Motor|PER#BAT#85|CUR#BAT#10" wird am | aufgeteilt. Das Ergebnis ist ein Array von Einzel-Einträgen.

Zweiter Split: den Eintrag aufteilen

string[] teile = eintraege[i].Split('#');
// teile[0] = Rover-ID  (z.B. "CUR")
// teile[1] = Typ       (z.B. "ERR")
// teile[2] = Wert      (z.B. "Motor")

Jeder Eintrag wird nochmal am # aufgeteilt. Die drei Teile bilden genau die Parameter für VerarbeiteStatus().

Die innere Schleife – manuell über die Liste iterieren

int k = 0;
while (k < roverListe.Count)
{
    Rover r = roverListe[k];
    if (r.GetId() == teile[0])
    {
        bool istKritisch = r.VerarbeiteStatus(teile[1], teile[2]);
        if (istKritisch)
        {
            wartung.ErstelleTicket(r.GetId(), "Kritischer Batteriestatus");
        }
    }
    k++;
}

Das Struktogramm schreibt while mit manuellem Zähler k vor – kein foreach. Mit roverListe[k] greift man per Index auf das jeweilige Rover-Objekt zu, genau wie bei einem Array. roverListe.Count ist das Listen-Äquivalent zu array.Length.

Stimmt die ID überein, wird VerarbeiteStatus() aufgerufen und der Rückgabewert direkt in istKritisch gespeichert. Nur wenn istKritisch wahr ist, wird ein Ticket beim WartungsDienst erstellt – die MissionsZentrale delegiert die Ticket-Erstellung vollständig an den spezialisierten Dienst.

Warum zwei while-Schleifen statt zwei foreach?

Das Struktogramm gibt es so vor. Im Abitur ist das die erwartete Übersetzung – auch wenn foreach hier technisch funktionieren würde. Wenn ein Diagramm explizit Zählervariablen zeigt, setzt man sie auch genauso um.

⚠️ **GitHub.com Fallback** ⚠️