Ein Onlineshop rechnet Versandkosten aus. Am Anfang gibt es genau eine Versandart, und die Rechnung passt in eine Zeile. Dann kommt Express dazu. Dann Abholung. Dann eine Sonderregel für Sperrgut.
Nach einem halben Jahr sieht die Methode so aus:
public decimal BerechneVersand(string art, decimal warenwert, double gewicht)
{
switch (art)
{
case "standard":
return warenwert > 100m ? 0m : 4.90m;
case "express":
return 12.90m + (decimal)gewicht * 0.5m;
case "abholung":
return 0m;
case "sperrgut":
return 39.00m + (decimal)gewicht * 1.2m;
default:
throw new ArgumentException($"Unbekannte Versandart: {art}");
}
}Das ist kein schlechter Code. Er ist lesbar, er funktioniert, und für vier Fälle würde ihn niemand anfassen. Trotzdem lohnt es sich, genau hinzusehen, was hier wächst.
Jede neue Versandart bedeutet eine Änderung an einer Methode, die bereits funktioniert. Du öffnest eine getestete Datei, um etwas hinzuzufügen, das mit den vorhandenen Fällen nichts zu tun hat. Und weil alle Fälle in einer Methode liegen, teilen sie sich auch alles andere: die Parameterliste, die Abhängigkeiten, die Tests.
Offen für Erweiterung, geschlossen für Änderung
Das ist das Open-Closed-Prinzip — das O in SOLID. Neues Verhalten sollst du hinzufügen können, ohne bestehendes zu verändern. Der switch oben verletzt genau das: ohne Änderung geht nichts.
Der Druck wird erst richtig sichtbar, wenn die Fälle unterschiedliche Dinge brauchen.
Sperrgut braucht die Abmessungen. Express braucht die Postleitzahl, weil manche Regionen nicht am nächsten Tag beliefert werden. Abholung braucht die Filiale. Also wandert alles in die Signatur:
public decimal BerechneVersand(
string art,
decimal warenwert,
double gewicht,
string? plz,
int? filialNummer,
Abmessungen? masse)Sechs Parameter, von denen jeder Aufruf drei mit null füllt. Das ist der Moment, an dem der switch aufhört, eine Vereinfachung zu sein.
Woran du den Fall erkennst
Drei Signale, und du brauchst nicht alle drei:
- Es gibt eine Entscheidung, die an mehreren Stellen im Code in derselben Form wiederkehrt (
switchüber denselben String, dieselbeif-Kette). - Die Zweige sind untereinander austauschbar — jeder liefert dasselbe Ergebnis in derselben Form, nur anders berechnet.
- Es kommen regelmässig neue Zweige dazu.
Wenn alle drei zutreffen, beschreibt der switch nicht eine Verzweigung, sondern eine Familie von Verfahren. Und Verfahren kann man in C# als Objekte behandeln.
Nicht jede Verzweigung ist ein Pattern
Ein switch über einen Enum-Wert, der sich seit drei Jahren nicht geändert hat, ist ein switch über einen Enum-Wert. Der Umbau lohnt sich, wenn die Fälle wachsen oder auseinanderlaufen — nicht, weil eine Verzweigung an sich verdächtig wäre.